The reservation object
A reservation is one party, on one service date, at one restaurant. The same
object comes back from every call on Reservations,
and it is what a reservation.* webhook delivers.
Fields
Section titled “Fields”objectstring
Possible values: reservation
The object type: always reservation.
idstring
The booking’s id, resv_…. Opaque and permanent. A booking converted from a hold keeps that hold’s suffix: hold_AbC becomes resv_AbC.
statusstring
Possible values: awaiting_guarantee awaiting_guest held pending confirmed partially_seated seated completed cancelled no_show
Where the booking is in its lifecycle.
⚠️ held and awaiting_guest never appear on a reservation this API serves — a
hold is its own object (GET /v1/holds) and a waitlist promotion awaiting its guest
is not yet a booking — so no endpoint, and no reservation.* webhook, can carry
either value. They are listed because the enum is the platform’s own and codegen
reads it verbatim. awaiting_guarantee DOES appear: it is a booking waiting for the
guest to save a card.
There is no late. The restaurant’s screens show a party as late from its start
time, but that is worked out on the spot and never stored, so a late party keeps the
status it had, usually confirmed.
sourcestring
Possible values: manual widget walk_in import waitlist api
The mechanism that created the booking. See booking_channel for the channel the
demand arrived through — a waitlist booking promoted by staff still has the channel
the guest joined through.
manual: entered by the restaurant’s staff.widget: made by the guest in the restaurant’s booking widget, including a widget embedded on a partner’s page.walk_in: a party seated without a booking.import: brought over from the restaurant’s previous reservation system.waitlist: came out of the waitlist.api: created through this API (POST /v1/reservations).
booking_channelstring
Possible values: back_office website_widget phone walk_in google_reserve partner_platform api
The channel the demand arrived through, as opposed to source, the mechanism.
back_office: taken by the restaurant’s staff.website_widget: the restaurant’s own booking widget.phone: taken by telephone.walk_in: a party that walked in.google_reserve: booked through Google.partner_platform: taken in the booking widget embedded on a partner’s own page.api: created through this API (POST /v1/reservations), including a hold converted into a booking.
party_sizeinteger
How many guests the booking is for.
service_datestring (date)
The service day the booking belongs to, YYYY-MM-DD in the restaurant’s timezone. A seating after midnight in a service that began the evening before keeps that evening’s date, so it can differ from the date in starts_at.
starts_atstring (date-time)
When the party is due to sit down: ISO 8601 with the restaurant’s UTC offset, e.g. 2026-07-01T20:00:00+02:00.
ends_atstring (date-time)optionalnullable
When the table is expected back, in the same format as starts_at. null when no seating duration applies to the booking.
guest_notesstringoptionalnullable
What the guest asked for, in their own words. Guest data: present only when your key
carries guests:read — on every booking alike, including the ones your key made.
Otherwise the field is absent, not null. Every webhook delivery carries it.
guest_notificationsstring
Possible values: service_managed partner_managed
Who writes to the guest about this booking. partner_managed means we send none of the discretionary guest messages for it. Treat the list as open.
excluded_from_shift_limitsboolean
True when this booking does not count against the shift’s covers caps — a private event seated apart from the room those caps describe.
Those caps do not refuse it either, so a party larger than the covers left is booked rather than turned away. It still holds its table, and it still appears in every count of guests.
Setting it requires the reservations:write:override scope, except on a
whole-section booking (entire_section_id), where it is the default.
contact_emailstringoptionalnullable
The e-mail address this booking’s messages go to, as given when it was made. null
means the booking uses the guest profile’s address. Guest data: present only when
your key carries guests:read — on every booking alike, including the ones your key
made. Otherwise the field is absent, not null. Every webhook delivery carries it.
contact_phonestringoptionalnullable
The phone number this booking’s messages go to, as given when it was made. null
means the booking uses the guest profile’s number. Present on the same terms as
contact_email.
internal_notesstringoptionalnullable
The restaurant’s own note about this booking — what its staff would type into the reservation form. Not shown to the guest anywhere.
Guest data, and gated exactly like guest_notes: present only when your key carries
guests:read — on every booking alike, including the ones your key made. Otherwise
the field is absent, not null. Every webhook delivery carries it.
gueststring or Guestnullable
The guest the booking is for. By default their id, gst_…; the full Guest object when
you pass ?expand=guest on GET /v1/reservations or GET /v1/reservations/{id}, which
needs the guests:read scope — without it the request is refused with 403 insufficient_scope, never downgraded to the id. Writes and webhook deliveries always
carry the id.
null when the booking is linked to no guest profile: a walk-in seated without
details, or a booking whose guest was deleted.
companyobjectnullable
The company the booking is made for, when it was filed under one — by the restaurant’s staff, or by company_id on your own create or PATCH. null otherwise, which is the common case; the key is always present. Readable by every key with reservations:read, unlike the guest’s own details.
objectstring
Possible values: company
The object type: always company.
idstring
The company’s id, cmp_….
namestring
The company’s name, as the restaurant recorded it.
tablesarray of Table
The tables the booking is seated at, each with its seating area. [] while no table is assigned.
guaranteeGuaranteeoptionalnullable
The card guarantee attached to the booking. null when it has none. Always included; no scope or expand needed.
metadataobjectoptional
Your own reconciliation data, echoed verbatim on every read and webhook; {} when
there is none.
Set once, when the booking is made, and never changed afterwards — PATCH refuses
it. It comes from metadata on POST /v1/reservations, or from the hold the
booking was converted from. A booking made in the restaurant’s own widget carries
metadata only when the widget was embedded by a partner platform that signed it into
its identity assertion; an unsigned widget booking never has any.
⚠️ Readable by every integration this restaurant has authorised, not only by the one that wrote it. Put reconciliation ids here, not commercial terms.
eventsarray of ReservationEventoptional
The booking’s lifecycle events, oldest first — the same list GET /v1/reservations/{id}/events returns. Present only when you pass ?expand=events on a read, which needs no scope beyond reservations:read; absent otherwise, never an empty stand-in.
modification_tokenstringoptional
The guest’s own credential for their booking page. That page is on the booking host
that public_booking_url in GET /v1/configuration names, not on this API, and the
link is {public_booking_url}/{modification_token}, the same link the confirmation
e-mail sends the guest.
Whoever holds it can do on that page everything the guest can: read, change or
cancel the booking and, while it is awaiting_guarantee, complete the card step.
Pass it only to the guest, as the link to their own page; never log it, display it
or keep it longer than you need it. The endpoints behind that page are the booking
widget’s, not part of this API.
⚠️ Present ONLY on the POST /v1/reservations response, and only when you ask with
?expand=modification_token and your key carries guests:read (it reads the
guest’s contact details, so it is gated like the guest book). Reads, lists and
webhooks never carry it — capture it at create time. Never null when present.
How long it lasts. It is minted once, when the booking is created, and never
rotates: the value you capture is the value that booking keeps. It carries no
expiry of its own and stays usable for as long as the booking is live, which in
practice means until the party has been and gone. Cancelling — by you, by the
guest or by a card request lapsing — retires it, and so does the restaurant
marking the booking a no-show: the page answers 404 from then on, exactly as it
does for a token that was never issued. There is no way to
revoke it while the booking stands, which is the other half of “never log it”:
if it leaks, the remedy is to cancel and rebook.
created_atstring (date-time)
When the booking was recorded: ISO 8601 with the restaurant’s UTC offset.
updated_atstring (date-time)
When anything on the booking last changed, in the same format. The value updated_since compares against.
Statuses and who moves them
Section titled “Statuses and who moves them”The status a booking is born in is the restaurant’s decision, not yours. Under
manual approval it lands pending. On a slot that carries a card guarantee it
lands awaiting_guarantee and the guest is emailed a payment link. Otherwise it
lands confirmed, which is the only value that means the table is booked.
Your key moves a booking two ways. A PATCH can push a confirmed booking back
to pending when the new slot or party size needs the restaurant’s approval, and
POST /v1/reservations/{id}/cancel makes it cancelled. Every other move is the
restaurant’s staff working its own floor: seating a party takes the booking
through partially_seated to seated, completed when the table is given back,
and no_show when nobody arrives. You see those as
events and as webhooks.
Three statuses are final for this API. PATCH on a completed, no_show or
cancelled booking is a 409 with
reservation_finalized, and so is
cancelling one that is completed or no_show. Cancelling a booking that is
already cancelled returns 200 and the same object, so a retry that lands
twice needs no special case.
What your key can read
Section titled “What your key can read”Four fields are guest data — guest_notes, internal_notes, contact_email
and contact_phone. A key without guests:read does not receive them on any
booking, and they are absent rather than null, so a null you do read is the
restaurant’s own empty value. The rule and the scope are on
Authentication.
Two fields arrive only when you ask for them. ?expand=guest replaces the guest
id with the whole guest object and needs guests:read; a
key without it is refused with 403, never quietly handed the id instead.
?expand=events attaches the lifecycle history and needs nothing beyond
reservations:read. See Expanding objects.
modification_token is narrower still: it appears on the 201 of
POST /v1/reservations, and nowhere else. Capture it there or lose it.
What is fixed once the booking exists
Section titled “What is fixed once the booking exists”id— permanent. A booking converted from a hold keeps that hold’s suffix.metadata— written at create, or carried over from the hold, and never changed. APATCHthat sends it is a400.guestandguest_id— the booking stays with the profile it was made for.guest_notifications— fixed for the life of the booking. A confirmation that has been sent cannot be unsent, and one that was suppressed cannot be sent late. Cancel and rebook for a different answer.sourceandbooking_channel— a record of how the booking arrived. This API never writes either, and sending one on a create is a400naming the field.
What a webhook carries
Section titled “What a webhook carries”Every reservation.* delivery puts the whole object in data.object, in the
same shape a GET returns — with three differences that catch people out:
- The four guest-data fields are on every delivery, whatever scopes the key behind the endpoint holds. An endpoint you register is a place you asked for guest data to be sent.
guestis always thegst_…id. A webhook never expands it, and there is noexpandto ask with.eventsandmodification_tokennever travel. Both exist only on a read you asked to expand, and on the create response respectively.
Webhooks covers registration and signing; the event catalog says which change fires which event type.