# VistaLink: hotel content

Brief for a coding agent. Show a full hotel page: gallery, rooms, policies, reviews and amenities for one hotel.

## Before you write code

- The API key comes from the environment as `VISTALINK_API_KEY`. Never hard-code it, never send it to a browser: call VistaLink from server code only.
- The user creates the key at https://vistalink.com/developers/onboarding. Test keys hit the real catalogue and spend credit; they never create Stripe charges.
- Base URL: https://api.vistalink.com. Every call sends `Authorization: Bearer $VISTALINK_API_KEY`.

## The call: GET /v2/hotels/{hotel_id}

Hotel detail.

```bash
curl 'https://api.vistalink.com/v2/hotels/43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19?sections=core%2Cimages%2Crooms%2Crates&check_in=2026-11-14&check_out=2026-11-16&adults=2&currency=EUR&search_id=srch_3f9c2a7e4b1d4c8a9e6f0b2d5a7c1e93' \
  -H "Authorization: Bearer $VISTALINK_API_KEY"
```

```typescript
const res = await fetch("https://api.vistalink.com/v2/hotels/43d0be71-5f3c-4c8e-9a52-1d7e2b6f0a19?sections=core%2Cimages%2Crooms%2Crates&check_in=2026-11-14&check_out=2026-11-16&adults=2&currency=EUR&search_id=srch_3f9c2a7e4b1d4c8a9e6f0b2d5a7c1e93", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.VISTALINK_API_KEY}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error.message);
const data = await res.json();
```

Starting from a link rather than a `hotel_id`? Call `POST /v2/hotels/resolve` first (free) and use the `hotel_id` it returns.

## What comes back

- `images`: a deduplicated, labelled gallery with the hero first.
- `rooms[]`: each room with its `listings[]` (where else it is sold), already matched.
- `policies`, `facts`, `reviews` (category scores and items), `location`, and cached `rates` when you pass dates.

## Rules that trip people up

- Ask only for what you render: `sections=core,images,rooms` keeps the response small.
- Pass the `search_id` from a recent search and the call is warm, at the same price.
- Start from a link instead of a `hotel_id`? Resolve it first with `POST /v2/hotels/resolve` (free): a listing URL, the hotel's own site, or a name and a city.

## Price

- `hotel.detail`: $0.005 per call

## When it fails

- Errors come back as `{ "error": { "code", "message", "request_id", "details" } }`. Log `request_id`; support needs it.
- Retry `rate_limited` (429) after `X-RateLimit-Reset`, and `upstream_error` (502) or `unavailable` (503) with backoff. Don't retry other 4xx: fix the request.
- `insufficient_credit` (402) means the account needs a top-up; `forbidden` (403) with `details.reason: "tier_required"` means the call needs a higher tier.

## Done when

- A hotel page renders its gallery in the order returned, hero first, with no duplicate photos.
- Each room shows where else it is sold, from `rooms[].listings[]`.
- Only the sections you render are requested (`sections=`).

## If you are an agent runtime rather than writing code

Connect the MCP server instead: `claude mcp add --transport http vistalink https://api.vistalink.com/mcp --header "Authorization: Bearer $VISTALINK_API_KEY"` (the shell expands the key into the stored config). Tools for this task: `get_hotel`, `resolve_hotel`. The tools ending in `_v2` share a name with a v1 tool (`search_hotels`, `get_rates`, `get_booking`, `chat_about_hotels`), so they carry the suffix. It stays until v1 is retired, so agents built on v1 keep working.

## Full reference

- Docs: https://vistalink.com/developers/docs/hotels
- OpenAPI: https://api.vistalink.com/v2/openapi.json
- Every brief: https://vistalink.com/developers/agents
