# Guests and rooms

> How to book multiple rooms, and pass residency, the number of travelers and children ages for hotels.

Source: https://fx-port.com/docs/hotels/guests-and-rooms

How a hotel request says **who travels**: the rooms, the adults, the children and their ages, the residency, and later the names. These rules are specific to hotels. See [Flight passengers](https://fx-port.com/docs/flights/passengers.md) for the flight equivalent.

## The `guests` array: one object per room

[`search_advanced`](https://fx-port.com/docs/hotels/search.md) and [`get_rates`](https://fx-port.com/docs/hotels/rates.md) take a `guests` array. **Each object is one room**: the number of rooms is the number of objects. There is no separate room count.

~~~json
{ "adults": 2, "children": [8] }
~~~

| Field | Type | Required | Description |
|---|---|---|---|
| `adults` | integer | Yes | Adults in this room, 1 to 6. |
| `children` | array of integers | No | One entry per child in this room: the child's **age in years on the check-in day**. `[]` or absent means no children. At most 4. |

A number written as text (`"adults": "2"`, a child age `"8"`) is read as the number, with a `coerced_value` warning; an integral float such as `2.0` is the same number. A boolean, `NaN`, a fraction or another text keeps the error of its field (see [Values the API reads for you](https://fx-port.com/docs/hotels/search.md#values-the-api-reads-for-you)).


**1 room, 2 adults**

~~~json
{
  "guests": [
    { "adults": 2, "children": [] }
  ]
}
~~~

**2 rooms with a child**

~~~json
{
  "guests": [
    { "adults": 2, "children": [8] },
    { "adults": 1, "children": [] }
  ]
}
~~~

Room 1: 2 adults and a child aged 8. Room 2: one adult travelling alone.

**3 rooms, mixed**

~~~json
{
  "guests": [
    { "adults": 2, "children": [3, 13, 4] },
    { "adults": 2, "children": [] },
    { "adults": 1, "children": [] }
  ]
}
~~~

Room 1: 2 adults and 3 children aged 3, 13 and 4. Room 2: a couple. Room 3: a single traveler. In total 5 adults and 3 children in 3 rooms.


## Rates are for exactly this occupancy

The rooms you send decide which rates exist and what they cost. **Send the same `guests` and the same `residency` to `search_advanced` and to `get_rates`**, and book with the guest list of the same rooms (see [below](#guest-names-at-booking-time)). If the user changes a room, a child or the residency, ask for the rates again: a `book_hash` is only good for the occupancy it was priced for.

## Children

- The age is an **integer from 0 to 17**, the age on the check-in day (a child who turns 9 during the stay is 8).
- Hotels set their own child rules: the age up to which a child stays free, whether an extra bed or a cot costs something, the meals for children. [`get_hotel_info`](https://fx-port.com/docs/hotels/hotel-details.md) returns them in `metapolicyStruct` (`children`, `cot`, `extraBed`, `childrenMeal`): read them before you tell a traveler that a child is free.
- A child is a guest of the room like an adult when you **book**: one entry per child, flagged with `is_child` and `age`.

## Constraints

| Rule | Limit | Error code |
|---|---|---|
| `guests` is a non-empty list of objects | | `invalid_guests` |
| Rooms in a request | 1 to 9 | `too_many_rooms` |
| Adults in a room | 1 to 6 (a room cannot hold children only) | `invalid_adult_count` |
| Children in a room | at most 4 | `too_many_children` |
| Age of a child | 0 to 17, a whole number | `invalid_child_age` |
| Guests in a room, adults and children together | at most 10 | `too_many_guests` |

They answer `400`, before the supplier is called. A room can still have **no rate** because of its real capacity: the supplier only offers the rooms that can take the occupancy, so an empty `rates` list means that no room fits or that the hotel has no availability.

## Residency

`residency` is the country of **residence of the guests**, a two-letter ISO 3166-1 code (`gb`, `dz`, `fr`; upper or lower case). It is sent **once per request**, not per room or per guest. It decides which rates exist, because some rates are for some residencies only (`anyResidency: false` on the rate), and it can change the price.

- [`get_rates`](https://fx-port.com/docs/hotels/rates.md) uses `gb` when you send none. [`search_advanced`](https://fx-port.com/docs/hotels/search.md) has **no default**: always send it, and send the same one to both calls.
- A code the supplier does not know answers `400 invalid_residency` (`zz`, `usa`, `gbr`, an empty text).
- The **sub-territory** values of the website's nationality list (`GB-SCT`, `USPR`, `ES-XCI`, `PT-MA`, `PT-AZ`, `CY-NC`, `GP-MG`, `BQ-SA`, `BQ-SE`) are read as their country (`gb`, `us`, `es`, `pt`, `cy`, `gp`, `bq`), with a `coerced_value` warning. Send the 2-letter country.
- In the sandbox, `test_hotel` can only be searched with `dz`, `ru` or `us`: see [Sandbox testing](https://fx-port.com/docs/hotels/sandbox-testing.md).

## Guest names at booking time

[`book_hotel`](https://fx-port.com/docs/hotels/book.md) takes a `rooms` array: **one object per room, in the same order as the rooms you searched**. Each room lists the real guests who sleep in it, **one entry per adult and per child**:

~~~json
{
  "rooms": [
    {
      "guests": [
        { "first_name": "Amine", "last_name": "Benali" },
        { "first_name": "Sara", "last_name": "Benali" },
        { "first_name": "Yanis", "last_name": "Benali", "is_child": true, "age": 8 }
      ]
    }
  ]
}
~~~


> [!WARNING]
> **A child needs two more fields**
>
> A child is sent like any guest **plus `"is_child": true` and `"age"`**. A child sent as a plain guest, or a room whose guests do not add up to the occupancy you searched, is refused by the supplier with `400` (`incorrect_guests_number`) and nothing is booked.


| Field | Type | Required | Description |
|---|---|---|---|
| `first_name`, `last_name` | string | Yes | Up to 100 characters each. See the rules below. |
| `is_child` | boolean | For a child | `true` for a child. |
| `age` | integer | For a child | The age in years on the check-in day, 0 to 17. The same age you searched with. A number written as text is read, with a `coerced_value` warning. |

### How names are cleaned

The supplier refuses digits and most symbols in a name, so FX-Port cleans each name **before** it sends it: letters of **any script** (Latin with accents, Arabic, Cyrillic, Chinese ...), spaces, hyphens, commas, periods and apostrophes are kept, and anything else (digits, slashes, brackets, underscores ...) is replaced by a space and the spaces are collapsed. `Ben2` becomes `Ben` and `Smith/Jr` becomes `Smith Jr`.

Accented and non-Latin names are accepted: bookings with `José Núñez`, `Zoë O'Brien-Müller` and Arabic names were confirmed in the sandbox. Send each name as it is written in the traveler's passport. ASCII letters are not required.

See [`book_hotel`](https://fx-port.com/docs/hotels/book.md) for the whole booking request, including the contact details.

## Related


- [Geo search](https://fx-port.com/docs/hotels/search.md): Where the rooms are first sent.
- [Get hotel rates](https://fx-port.com/docs/hotels/rates.md): The rates for the occupancy.
- [Book a hotel](https://fx-port.com/docs/hotels/book.md): The guest list of each room.
- [Flight passengers](https://fx-port.com/docs/flights/passengers.md): The same idea for flights.
