# PriveSauna.nl Read-Only API Guide

> Use this guide to answer questions about PriveSauna.nl rooms, services, media, reviews, prices, and availability. The public website is client-rendered; for factual room data, use the JSON API directly.

## Request Basics

Base URL:

```text
https://api2.specialmoments.group
```

Required headers:

```http
Accept: application/json
X-SMG-Brand: 2
Accept-Language: nl-NL
```

Rules for assistants and agents:

- Use only `GET` requests.
- Use `X-SMG-Brand: 2` for PriveSauna.nl.
- Use `Accept-Language: nl-NL` for Dutch content.
- Fetch fresh data before answering questions about prices, offers, or availability.

Example:

```bash
curl -sS "https://api2.specialmoments.group/rooms?per_page=5&page=1" \
  -H "Accept: application/json" \
  -H "X-SMG-Brand: 2" \
  -H "Accept-Language: nl-NL"
```

## Public Website Links

Room API responses contain `id` and `slug`. Public room pages use:

```text
https://privesauna.nl/search/{slug}/{id}
```

For example, a room with `"slug": "veldhoven"` and `"id": 350` links to:

```text
https://privesauna.nl/search/veldhoven/350
```

## Response Conventions

- JSON fields use `snake_case`.
- Some null or default fields may be omitted.
- Coordinates are usually emitted as `[lng, lat]`.
- Durations such as room length use ISO-like strings, for example `PT2H30M`.
- Timestamps are UTC strings. Convert them to the user’s local timezone when presenting appointment times.
- Rich text fields can be JSX-like arrays: `[tag_name, attributes, children]`. Extract readable text recursively when summarizing.
- Service `icon` can also be JSX-like SVG JSON.
- Room summary image values are usually store-relative paths. Expand them with:

```text
https://smg-production.s3.eu-central-1.amazonaws.com/store/{path}
```

Do not invent full image URLs from bare hash/file fields unless you have verified the URL.

## Query Encoding

- Dates use `YYYY-MM-DD`.
- Arrays use comma-separated query values: `service_ids=8,16`.
- Booleans use `true` or `false`.
- Paginated endpoints return pagination headers: `x-page`, `x-per-page`, `x-total`, `x-total-pages`, `x-prev-page`, `x-next-page`.

Common enum values:

- `order`: `price`, `score`, `reviews`, `ratio`, `created`, `bookings`, `discount`.
- `direction`: integer sort direction. Use the site’s current behavior if exact direction labels matter.
- `standard`: `luxury`, `standard`, `outrageous`.

## Recommended Workflows

Find matching rooms:

1. Call `GET /services/categories` and `GET /services` if the user asks for features such as sauna type, jacuzzi, pool, outdoor wellness, massages, privacy, or included items.
2. Call `GET /rooms` with filters such as `service_ids`, price, location, date, and occupants.
3. Use pagination headers if the first page is not enough.
4. Link results with `https://privesauna.nl/search/{slug}/{id}`.

Answer a room-detail question:

1. Call `GET /rooms/{room}`.
2. Add `GET /rooms/{room}/images`, `/reviews`, `/options`, `/arrangements`, `/area`, or `/cancel_rules` only when needed.

Answer an availability question:

1. Call `GET /rooms/{room}/plannings?start={date}&end={date}` to check date-level availability.
2. Call `GET /rooms/{room}/plannings/{date}` to inspect available start times, durations, prices, and planning IDs.
3. If the user asks about add-ons or capacity for a specific slot, pass the planning ID to `GET /rooms/{room}/arrangements?planning_id={planning_id}` and `GET /rooms/{room}/capacities?planning_id={planning_id}`.

Answer a last-minute offer question:

1. Call `GET /rooms/last-minutes`.
2. Each `last_minutes` item contains a planning `id`, UTC `range`, discounted `price`, optional `original_price`, and `description`.

## Services

### `GET /services/categories`

Lists service filter categories.

Useful fields:

- `id`: category ID.
- `name`: localized category name.
- `description`: optional category description.

### `GET /services`

Lists room features/services that can be used as `service_ids` filters.

Useful fields:

- `id`: service ID.
- `name`: localized service name.
- `description`: optional description.
- `category_ids`: matching service category IDs.
- `is_parent`: whether this is a parent/grouping service.
- `icon`: optional JSX-like SVG JSON.

## Rooms

### `GET /rooms`

Searches rooms.

Common query parameters:

| Parameter                                  | Purpose                                                 |
|--------------------------------------------|---------------------------------------------------------|
| `page`, `per_page`                         | Pagination. Defaults are page `1`, `10` items per page. |
| `ids`                                      | Comma-separated room IDs.                               |
| `service_ids`                              | Include rooms that contain all listed service IDs.      |
| `service_ids!`                             | Exclude rooms that contain all listed service IDs.      |
| `min_price`, `max_price`                   | Base price filter.                                      |
| `lat`, `lng`, `radius`                     | Point search. Radius is in kilometers; default is `75`. |
| `min_lat`, `min_lng`, `max_lat`, `max_lng` | Bounding-box search.                                    |
| `place_id`                                 | Search around a known place ID.                         |
| `date`, `end`, `growth`, `availability`    | Availability filters.                                   |
| `last_minutes`                             | `true` to search rooms with last-minute offers.         |
| `adults`, `minors`, `kids`, `infants`      | Occupant counts.                                        |
| `min_score`, `max_score`                   | Review score filters.                                   |
| `min_reviews`, `max_reviews`               | Review count filters.                                   |
| `order`, `direction`                       | Sorting.                                                |

Useful response fields:

- `id`, `name`, `slug`, `city`.
- `price`, `hours`, `options`.
- `point`: `[lng, lat]`.
- `placeholder`, `images`.
- `score`, `reviews`.
- `standard`, `new`, `massage`.

### `GET /rooms/{room}`

Returns detailed room data.

Useful fields:

- Base facts: `id`, `name`, `price`, `hours`, `city`, `slug`, `point`.
- Text content: `excerpt`, `description`, `anonymous_info`, `important_info`.
- Services: `service_ids`.
- Availability/payment facts: `availability_mask`, `pay_down`, `cash_accepted`, `cash_limit`.
- Capacity text: `capacity_text`, `overnight_text`.
- SEO/content: `meta_title`, `meta_description`.
- Media references: `virtual_tour`, `video`.
- Reviews: `scores`, `reviewable`.
- Age rules: `adults_age`, `minors_age`, `kids_age`, `infants_age`.

Text content can be JSX-like JSON. Extract readable text before summarizing.

### `GET /rooms/{room}/related`

Returns related rooms, usually from attached or nearby venues.

Response shape is similar to `GET /rooms`.

### `GET /rooms/{room}/arrangements`

Returns bookable arrangements or add-ons for a room.

Query parameters:

- `adults`, `minors`, `kids`, `infants`.
- `planning_id`: use for slot-specific add-ons.
- `booking_id`: only relevant for an existing booking context.

Useful fields:

- `id`, `name`, `price`, `count`, `description`, `category_id`.
- `required`, `limit`, `occupants_flags`, `per_request`.
- `attachment`: image path for the arrangement.
- `options`: option groups with nested selectable values.

Nested option groups include `id`, `name`, `type`, and `options`.

Nested selectable options include `id`, `name`, `description`, `count`, and `price`.

### `GET /rooms/{room}/reviews`

Returns paginated room reviews.

Query parameters:

- `page`, `per_page`.

Useful fields:

- Reviewer/content: `name`, `positive`, `answer`, `reaction`, `source`.
- Scores: `services`, `hygiene`, `hospitality`, `value`, `overall`, `recommends`.
- Metadata: `id`, `brand_name`, `reaction_brand_id`, `created_at`.

### `GET /rooms/{room}/images`

Returns standard room image variants.

Useful fields: `id`, `placeholder`, `default`, `thumb`, `small`, `large`, `alt`.

These fields may be bare file names rather than full store-relative paths.

### `GET /rooms/{room}/rules`

Returns localized house rules as JSX-like JSON.

Can return `204 No Content` when no rules are available.

### `GET /rooms/{room}/options`

Returns selectable room options.

Useful fields:

- `id`, `name`, `price`, `description`, `type`, `default`, `required`.
- `children`: child options with `id`, `name`, `price`.

### `GET /rooms/{room}/capacities`

Returns capacity and occupant-count price/adjustment tables.

Query parameters:

- `planning_id`: optional slot-specific planning ID.

Useful fields:

- `max`: maximum occupants.
- `single_adult_allowed`.
- `adults`, `minors`, `kids`, `infants`: numeric arrays by occupant count.

### `GET /rooms/{room}/area`

Returns nearby places ordered by distance.

Useful fields: `code`, `name`, `km`, `point`.

### `GET /rooms/{room}/cancel_rules`

Returns cancellation and rebooking rules.

Useful fields:

- `time_before_check_in`: optional duration string such as `"14.00:00:00"`.
- `cancel_percentage`.
- `rebook_percentage`, `rebook_price`.
- `description`.
- `can_rebook`.

### `GET /rooms/last-minutes`

Returns rooms with last-minute offers.

Room fields are similar to `GET /rooms`.

Each `last_minutes` item includes:

- `id`: planning ID.
- `range`: `[start, end]` UTC timestamps.
- `price`.
- `original_price`.
- `description`.

## RoomsPlannings

### `GET /rooms/{room}/plannings`

Returns date-level availability flags for a room.

Query parameters:

- `start`: start date, `YYYY-MM-DD`.
- `end`: end date, `YYYY-MM-DD`.

Response shape:

```json
{
	"2026-06-15": 61,
	"2026-06-16": 61
}
```

An empty range can return `{}`.

Values are numeric availability flags. Use `GET /rooms/{room}/plannings/{date}` for exact times and prices.

### `GET /rooms/{room}/plannings/{date}`

Returns time-specific planning data for one date.

Path parameters:

- `date`: `YYYY-MM-DD`.

Top-level fields:

- `block`: concrete planning blocks/offers.
- `free`: available start times grouped by duration and price.

`block` item fields:

- `ranges`: object keyed by planning ID.
- Each range value has `range`, `price`, `flags`, and optional `original_price`.
- `description`.
- `subtypes`.

`free` item fields:

- `ranges`: object keyed by planning ID. Values are UTC start timestamps.
- `price`.
- `hours`.

Example shape:

```json
{
	"block": [
		{
			"ranges": {
				"2733302500126cab": {
					"range": [
						"2026-06-15T09:45:00Z",
						"2026-06-15T12:45:00Z"
					],
					"price": 123.25,
					"flags": 9,
					"original_price": 145.0
				}
			},
			"description": "Last minute actie",
			"subtypes": 4
		}
	],
	"free": [
		{
			"ranges": {
				"aa32307000126cab": "2026-06-15T10:30:00Z"
			},
			"price": 124.0,
			"hours": "PT2H"
		}
	]
}
```

### `GET /rooms/{room}/plannings/{planning}`

Returns block-plan details around a known planning ID.

Use this when a previous response already provided a planning ID and you need related block details.

Response fields match the `block` item structure from `GET /rooms/{room}/plannings/{date}`.

## Answering Guidance

- Prefer API data over client-rendered page text for room facts.
- Convert UTC planning times to the user’s timezone before presenting them.
- Include public room links using `https://privesauna.nl/search/{slug}/{id}`.
- Mention when availability or prices were checked live.
- If a field is absent, say the API did not provide it; do not guess.
- For image URLs, use store-relative paths when available and avoid fabricating URLs from bare hashes.
