Skip to content

Book a whole room

A corporate dinner of sixty that has the salon to itself does not fit on a table. No single table or combination seats sixty, so an ordinary create is refused with no_table_available, and no override reaches that code. Send entire_section_id instead, and the booking takes every table in the section, whatever the party size.

You name the section. The restaurant’s floor plan decides which tables are in it, read at the moment you book, from the floor plan in use for that service. A table the restaurant draws into the room after your booking is not part of it.

Scope Why this flow needs it
reservations:read GET /v1/sections, to find the room.
reservations:write The create and the cancel.
reservations:write:override entire_section_id on the create.

The second of those two write scopes is separate because a whole-room booking takes every table in the room away from the widget, from the restaurant’s other integrations and from its own table assignment. It does not imply reservations:write, which a key needs beside it. A key without it that sends entire_section_id is refused 403 insufficient_scope with param: "entire_section_id". That check runs before the section is looked up, so the refusal never tells you whether the id exists.

reservations:write:override also carries the override array and excluded_from_shift_limits. A restaurant that grants a key the power to book a room whole has granted it those too; there is no narrower grant that stops at rooms.

The scope reaches sections the restaurant keeps off online booking. A private room is usually bookable_online: false, so the widget never offers it, and a whole-room booking takes it all the same.

Terminal window
curl "https://api.useservice.app/v1/sections" \
-H "Authorization: Bearer $SERVICE_API_KEY"
{
"object": "section",
"id": "sec_7Yd2Qm9",
"name": "Salon",
"area_type": "private_room",
"bookable_online": false,
"group_id": "sec_7Yd2Qm9"
}

Rows sharing a group_id are the same room drawn on different floor plans. The group_id is what you pass as entire_section_id.

Service publishes no capacity figure for a room — not how many it seats, not how many it holds standing. A floor plan counts chairs, and a chair count does not know that two four-tops pushed together seat more or fewer than eight, nor how many people stand in the room once the chairs are gone. How many fit is between the restaurant and whoever is hiring the room.

Nothing here says the room is free. See Find out whether the room is free.

A room can exist and still be out of reach on the night you want. Each service uses one floor plan, a date override can swap it, and only the rooms drawn on that plan can be booked whole.

Read section_ids on the service the guest picked, in GET /v1/availability for that date:

Terminal window
curl -G "https://api.useservice.app/v1/availability" \
-H "Authorization: Bearer $SERVICE_API_KEY" \
--data-urlencode "service_date=2026-10-14" \
--data-urlencode "party_size=60"
{
"object": "availability_shift",
"name": "Dîner",
"section_ids": ["sec_7Yd2Qm9", "sec_3Hn6Vc1"]
}

section_ids holds the group_id of every room drawn on the floor plan that service uses on that date. Match each one to the rows of GET /v1/sections sharing that group_id, and pass it as entire_section_id. It does not depend on party_size or section_id, and it ignores bookable_online, which a whole-room booking does not read — so a private room sold as a hire is listed there even though a guest cannot ask to sit in it. It is empty when the plan in use has no sections.

A room missing from section_ids is refused with no_table_available_in_section for that service on that date. A room listed there is not a free room: another booking can hold one of its tables, and you learn that only by attempting the booking.

Decide which rooms to offer for hire from section_ids alone.

Terminal window
curl -X POST "https://api.useservice.app/v1/reservations" \
-H "Authorization: Bearer $SERVICE_API_KEY" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
-d '{
"service_date": "2026-10-14",
"start_time": "19:30",
"party_size": 60,
"entire_section_id": "sec_7Yd2Qm9",
"duration": "until_shift_end",
"guest": { "first_name": "Marie", "last_name": "Dupont", "email": "[email protected]" },
"guest_notes": "Séminaire — cocktail debout, vidéoprojecteur"
}'

The 201 is an ordinary reservation, and its tables array lists every table in the room. entire_section_id is not a stronger section_id: section_id asks for a table somewhere in a section, and sending both is a 400.

No ceiling is applied to it. party_size is not measured against the room, and the max_party_size on GET /v1/configuration — the cap an ordinary create is held to — is not applied here, so party_size_too_large does not arise on a whole-room booking. A party of sixty in a room whose tables seat forty is accepted with no override, because a reception standing in a cleared room is a thing the floor plan cannot see. The restaurant knows what its own salon fits, and it agreed the hire before you sent it.

One gate survives, and it is the lower one. A party below the restaurant’s minimum is still refused party_size_too_small, overridable as any booking’s is. The service’s covers caps do not bound a hire either, so sixty at a service with forty covers left is booked rather than turned away. See Shift limits.

Without duration, the booking lasts as long as the restaurant’s booking rules give a party that size, like any booking. duration: "until_shift_end" holds the room until the service closes. It is the only value duration accepts, duration is accepted only beside entire_section_id, and there is no field for an end time.

until_shift_end moves the end and not the start. start_time is still one of the service’s seating times, as GET /v1/availability lists them: 19:10 at a service that seats every half hour is refused with start_time_off_grid, while the 19:30 above is accepted.

A whole-room booking is excluded from the service’s covers caps by default: the event in the salon does not count against the main room’s covers. Send excluded_from_shift_limits: false to count it. Neither spelling is refused for a key that could send entire_section_id in the first place: the same scope carries both.

The exclusion is what lets a service already selling take the room: the covers left are not measured against the hire, as A corporate dinner needs one field explains. Send excluded_from_shift_limits: false and they are, like any other booking. The other booking rules (cutoff, advance window, a blocked slot) are checked as on any create, and the overridable ones are waived with override.

If any booking holds one of the room’s tables during the window this booking would hold, the create is refused and nothing is placed. A whole-room booking is never placed on part of a room.

{
"error": {
"type": "invalid_request_error",
"code": "booking_rules_violated",
"violations": [
{
"code": "section_occupied",
"message": "…",
"param": "entire_section_id",
"overridable": false,
"conflicts": [
{ "table": "tbl_4Rk8Wz2", "start_time": "19:00", "end_time": "21:00" }
]
}
]
}
}

Each entry in conflicts is one occupied table and the time it is held from and to. Entries are ordered by table, then start time, so two refusals differ only when the floor has changed. table is null when the room has no tables and another whole-room booking holds it. Nothing in an entry says who holds the table: no guest, no booking id, no party size.

The window is the one this booking would hold. With until_shift_end, a party seated at 22:30 is in the way; without it, a booking whose rules end it at 22:00 is clear of that party.

section_occupied is not overridable, and naming it in override is a 400. The remedies are a different time or date, or the restaurant moving the parties listed. The restaurant sees them on its own floor plan.

A section that exists but is not drawn on the floor plan in use for that service (a terrace missing from the winter plan) is refused with no_table_available_in_section, param: "entire_section_id". section_ids on the service tells you so before you try, as Which rooms each service offers explains.

No endpoint answers it. GET /v1/availability answers which table fits a party, so for a party of sixty it has no slot to offer, and its section_ids says which rooms the service can be booked whole in, not which are taken. Attempt the booking: a section_occupied refusal and its conflicts are the answer, and a refused attempt books nothing.

  • It cannot be modified. PATCH /v1/reservations/{id} on a whole-room booking answers 409 section_booking_not_modifiable, for any change, including guest_notes alone. (An empty body, or one naming a field no PATCH accepts, gets its 400 first.) To change it, cancel and rebook.
  • Cancelling gives every table back. POST /v1/reservations/{id}/cancel works as on any booking.
  • It cannot be held. POST /v1/holds carrying entire_section_id or duration is a 400 naming the field. Book the room directly.
  • An ordinary booking cannot become one. entire_section_id or duration on a PATCH of any booking is a 400 naming the field.

The reservation.created event records the claim, which you can read back on GET /v1/reservations/{id}/events:

{
"object": "reservation_event",
"type": "reservation.created",
"data": {
"source": "api",
"party_size": 60,
"excluded_from_shift_limits": true,
"booked_entire_section": "sec_7Yd2Qm9",
"duration": "until_shift_end"
}
}

booked_entire_section is the room as drawn on that night’s floor plan. duration is present only when you asked for one.

Code Where it arrives What to do
insufficient_scope, param: "entire_section_id" 403 Ask the restaurant for a key carrying reservations:write:override.
bad_request 400 section_id or hold_id beside entire_section_id, duration without it, or a duration other than until_shift_end. param is entire_section_id or duration.
section_occupied inside violations, overridable: false Read conflicts. A different time or date, or the restaurant moves somebody.
no_table_available_in_section inside violations, overridable: false The room is not on that service’s floor plan. Pick from section_ids on the service in GET /v1/availability.
section_booking_not_modifiable 409 on PATCH Cancel and rebook.