# pickthehotel — intents.md

pickthehotel is an **answer engine for hotels**. Ask for the single best hotel that fits an intent in a
given city, and get one confident pick — decided by automated signals, never paid placement.

## Intent: `hotel.pick`

Return the best hotel in a city for one of four intents.

**HTTP**

```
POST https://pickthehotel.com/intents
Content-Type: application/json

{ "city": "zurich", "mode": "iconic" }
```

**Parameters**

| field | values | notes |
|---|---|---|
| `city` | `zurich` | more cities rolling out; `GET /intents` lists what's live. An unknown city returns `400`. |
| `mode` | `iconic` \| `value` \| `budget` \| `family` | see below. An unknown mode returns `400`; omitting it defaults to `value`. |

**Party:** picks are pre-computed for a **standard 2-adult stay**, and `family` is pre-filtered for **2 adults + 2 children in one room**. Arbitrary party sizes aren't supported yet — the response echoes the party the answer was actually computed for in `query.computedFor`.

**Modes**

- `iconic` — the landmark, the obvious best in town (prestige + class + location).
- `value` — great and fairly priced (rating measured against price).
- `budget` — cheap, clean, and genuinely central.
- `family` — a room that actually sleeps 2 adults + 2 children in one room (hard-filtered on live availability).

**Response** (abridged)

```json
{
  "ok": true,
  "intent": "hotel.pick",
  "query": { "city": "Zurich", "mode": "iconic", "computedFor": { "adults": 2 }, "stay": { "checkin": "2026-10-16", "checkout": "2026-10-18", "nights": 2 } },
  "pick": {
    "name": "Baur au Lac",
    "rating": 9.1, "ratingBasis": "531 reviews (adjusted)",
    "pricePerNight": { "amount": 1062, "currency": "CHF" },
    "priceComparison": { "referencePerNight": 1189, "currency": "CHF", "youSavePct": 11, "source": "booking.com" },
    "distanceToCentreKm": 0.18,
    "book": "https://pickthehotel.com/go/lp...?mode=iconic",
    "why": { "prestige": 1.0, "quality": 0.94, "central": 0.98 },
    "disclosure": "affiliate — pickthehotel earns a commission if you book through this link"
  },
  "alternatives": [ "... up to 3 more ..." ]
}
```

Send the traveller to `pick.book`. It records the match and redirects to the booking page.
All prices are **per night** (averaged over a sample 2-night stay); `priceComparison.referencePerNight` and its `source` state what the saving is measured against. The `why` object's keys are **mode-dependent** — treat it as an open map. Possible signals: `prestige`, `quality`, `central`, `popularity`, `value`, `cheap`, `brand`.

## How the pick is decided

Every hotel in the city is scored on the same automated signals — so the answer is defensible and scales:

- **value** = guest-rating percentile − price percentile (punches above its price)
- **location** = closeness to the town centre, old town + main church, and the lake (the station counts, but sitting on top of it is not "central")
- **quality** = review score, **Bayesian-shrunk** toward the city average by review count (a perfect score from 90 guests never beats 9.2 from 4,000)
- **popularity** = review volume · **prestige** = star class · **cheap** = outright price
- **family fit** = a hard filter on live availability for the whole party in one room
- reliable mid-market chains get a small nudge, but only after clearing every other bar

No hand-curation. No paid placement.

## Settlement & disclosure

Booking links are affiliate: pickthehotel earns a commission if you book. This is disclosed on every pick.
Settlement is reported back into the [intent graph](https://github.com/Goldcote/intent-graph) via `/postback`
(network-postback tier). Agents may record their own edges at `POST /edges`:

```
POST /edges   { "type": "fetch|shortlist|click", "offer": "<hotel id or offer key>", "intent": "hotel.pick", "meta": { ... } }
```

`type` and `offer` are required (`settle`/`refund`/`dispute` are rejected here — those come only from `/postback`).

See also: [/llms.txt](/llms.txt) · [/agents.md](/agents.md)
