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.
Scopes for this job
Section titled “Scopes for this job”| 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.
Find the room
Section titled “Find the room”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.
Which rooms each service offers
Section titled “Which rooms each service offers”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:
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.
Book it
Section titled “Book it”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.
The party size
Section titled “The party size”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.
How long it holds the room
Section titled “How long it holds the room”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.
Shift limits
Section titled “Shift limits”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.
A room that is partly taken
Section titled “A room that is partly taken”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.
Find out whether the room is free
Section titled “Find out whether the room is free”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.
After the booking
Section titled “After the booking”- It cannot be modified.
PATCH /v1/reservations/{id}on a whole-room booking answers409 section_booking_not_modifiable, for any change, includingguest_notesalone. (An empty body, or one naming a field noPATCHaccepts, gets its400first.) To change it, cancel and rebook. - Cancelling gives every table back.
POST /v1/reservations/{id}/cancelworks as on any booking. - It cannot be held.
POST /v1/holdscarryingentire_section_idordurationis a400naming the field. Book the room directly. - An ordinary booking cannot become one.
entire_section_idordurationon aPATCHof any booking is a400naming 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.
The refusals this flow produces
Section titled “The refusals this flow produces”| 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. |