Skip to content

Retrieve a reservation

GET
/v1/reservations/{id}
curl --request GET \
--url https://api.useservice.app/v1/reservations/example \
--header 'Authorization: Bearer <token>'

Retrieves one reservation by its public id. Requires the reservations:read scope. Ids this API hands you always resolve here, including the id in the 201 of a POST /v1/reservations that landed awaiting_guarantee. An id that names a booking the restaurant never had — an abandoned widget card step, an unclaimed waitlist offer — is a 404, the same answer as an unknown id.

contact_email, contact_phone and guest_notes are included only when your key carries guests:read — on this booking as on any other. Otherwise they are absent.

id
required
string

Reservation public id (resv_…)

expand
Array<string>
Allowed values: guest events

Relationships to inline; repeat or comma-separate (?expand=guest,events). Allowed values are per-resource. expand=guest inlines the full guest profile and therefore requires the guests:read scope: without it the request is refused with 403 insufficient_scope and param: "expand", never silently downgraded to the bare guest id.

Service-Version
string

Pin this request to a dated version of the contract, e.g. 2026-09-01. Omit it and the version pinned on the API key applies. A version we do not publish is a 400 bad_request with param: "Service-Version". The version that was applied comes back on the Service-Version response header, always.

Reservation found

Media typeapplication/json
object
object
required

The object type: always reservation.

string
Allowed values: reservation
id
required

The booking’s id, resv_…. Opaque and permanent. A booking converted from a hold keeps that hold’s suffix: hold_AbC becomes resv_AbC.

string
status
required

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.

string
Allowed values: awaiting_guarantee awaiting_guest held pending confirmed partially_seated seated completed cancelled no_show
source
required

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).
string
Allowed values: manual widget walk_in import waitlist api
booking_channel
required

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.
string
Allowed values: back_office website_widget phone walk_in google_reserve partner_platform api
party_size
required

How many guests the booking is for.

integer
service_date
required

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.

string format: date
starts_at
required

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.

string format: date-time
ends_at

When the table is expected back, in the same format as starts_at. null when no seating duration applies to the booking.

string format: date-time
nullable
guest_notes

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.

string
nullable
guest_notifications
required

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.

string
Allowed values: service_managed partner_managed
excluded_from_shift_limits
required

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.

boolean
contact_email

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.

string
nullable
contact_phone

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.

string
nullable
internal_notes

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.

string
nullable
guest
required
Any of:

The guest’s id, gst_… — the unexpanded form.

string
company
required

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.

object
object
required

The object type: always company.

string
Allowed values: company
id
required

The company’s id, cmp_….

string
name
required

The company’s name, as the restaurant recorded it.

string
tables
required

The tables the booking is seated at, each with its seating area. [] while no table is assigned.

Array<object>
object
object
required

The object type: always table.

string
Allowed values: table
id
required

The table’s id, tbl_…. Opaque and stable.

string
name
required

The table’s name on the restaurant’s floor plan.

string
min_capacity
required

The smallest party the table is meant for. Always a number, never null.

integer
max_capacity
required

The largest party the table seats on its own — a larger party needs tables joined together. Always a number, never null, and never below min_capacity.

integer
section
required

The seating area the table is in.

object
object
required

The object type: always section.

string
Allowed values: section
id
required

Pass this as section_id to GET /v1/availability or to a create — any member of a group resolves to the whole group, so a sec_… read off a reservation’s table works without knowing which row it is.

string
name
required

The area’s name, in the restaurant’s primary_language.

string
area_type
required

The kind of space, as the restaurant classified it: indoor, outdoor, terrace, bar, lounge, private_room (a room of its own), patio or rooftop. For a group, take it from the row where group_id == id.

string
Allowed values: indoor outdoor terrace bar lounge private_room patio rooftop
bookable_online
required

Whether THIS row is offered for online booking. Per row, never per group: it is deliberately not cascaded between group members, so a group is worth offering when ANY of its rows is true.

boolean
group_id
required

The sec_… id that identifies the conceptual seating area this row belongs to.

Rows sharing a group_id are ONE choice to a guest — the same area drawn on more than one floor plan — so group by it before rendering a picker, or the same area is offered twice.

Exactly one row per group has group_id == id: take that row’s name and area_type as the group’s. The others are kept in step by the product and are normally identical, but that is a behaviour of its editing paths rather than a constraint, so the naming row is the answer that cannot go stale.

string
guarantee

The card guarantee attached to the booking. null when it has none. Always included; no scope or expand needed.

object
object
required

The object type: always guarantee.

string
Allowed values: guarantee
kind
required

What the guest is guaranteeing with.

  • imprint: a saved card, charged only on a no-show or a late cancellation.
  • prepayment: payment up front. Reserved: no guarantee is issued with it today.
string
Allowed values: imprint prepayment
state
required

Where the guarantee is in its own lifecycle, which is separate from the booking’s status.

  • awaiting_card: waiting for the guest to save a card, until expires_at.
  • active: a card is saved and can be charged under the terms the guest accepted.
  • expired: the guest did not save a card in time.
  • withdrawn: the restaurant withdrew the request before a card was saved.
  • released: the card was let go and nothing will be charged.
  • charged: the full amount was charged.
  • partially_charged: less than amount was charged.
  • charge_failed: a charge was attempted and declined; it may be retried.
  • refunded: money charged was refunded in full.
  • disputed: the guest disputed the charge with their bank.
string
Allowed values: awaiting_card active expired released charged partially_charged charge_failed refunded disputed withdrawn
origin
required

How the card was asked for.

  • online_booking: the guest saved it while booking in the restaurant’s widget.
  • staff_request: the guest was sent a link to save it — by the restaurant, or because the booking was created through this API.
  • waitlist_offer: the guest saves it to claim a place offered from the waitlist.
string
Allowed values: online_booking staff_request waitlist_offer
amount
required

The most that can be charged, as the guest accepted it: in the minor unit of currency (cents), so 4000 is €40.00. Covers the whole party.

integer
currency
required

ISO 4217 currency code of every amount on this object, e.g. EUR.

string
charged_amount
required

How much has been charged so far, in minor units of currency. 0 when nothing has.

integer
refunded_amount
required

How much of charged_amount has been refunded, in minor units of currency. 0 when nothing has.

integer
card
required

null until the guest saves a card, and on a guarantee that never had one. Once a card is saved, its brand and the last four digits of its number; last4 can be null when the card network does not report it.

object
brand
required

The card network, as the payment processor reports it, e.g. visa.

string
last4
required

The last four digits of the card number.

string
nullable
cancel_deadline_at
required

Until when the guest can cancel without being charged: ISO 8601 with the restaurant’s UTC offset. A cancellation after it may be charged. null means cancelling is always free and only a no-show is charged.

string format: date-time
nullable
expires_at
required

While state is awaiting_card, the deadline for the guest to save a card, in the same format. null in every other state.

string format: date-time
nullable
consented_at

When the terms the guest accepts were recorded, in the same format: when the card was saved in the booking widget, or when the request was sent for a card asked for by link. null on a widget booking whose card is not saved yet.

string format: date-time
nullable
created_at
required

When the guarantee was created, in the same format.

string format: date-time
updated_at
required

When the guarantee last changed, in the same format.

string format: date-time
metadata

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.

object
key
additional properties
any
events

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.

Array<object>
object
object
required

The object type: always reservation_event.

string
Allowed values: reservation_event
id
required

The event’s id, evt_…. Opaque and permanent. Not the id of the webhook event that announced the same change: the two are numbered separately.

string
type
required

What happened, named like the webhook event for the same change.

  • reservation.created: the booking was made.
  • reservation.pending: it became a request awaiting the restaurant’s decision.
  • reservation.confirmed: the restaurant accepted it.
  • reservation.declined: the restaurant turned the request down.
  • reservation.updated: its details or its tables changed.
  • reservation.partially_seated: part of the party was seated.
  • reservation.seated: the party was seated.
  • reservation.completed: the party left.
  • reservation.cancelled: it was cancelled — data.cancellation_reason says how.
  • reservation.no_show: the party did not come.
string
Allowed values: reservation.created reservation.confirmed reservation.declined reservation.updated reservation.pending reservation.partially_seated reservation.seated reservation.completed reservation.cancelled reservation.no_show
created_at
required

When it happened: ISO 8601 with the restaurant’s UTC offset.

string format: date-time
data
required

What changed, keyed per event type — statuses as from_status/to_status, field edits under changes. Internal ids, staff identity and the staff’s own notes are stripped. Treat the key set as open.

Six keys carry the guest’s own data and appear only when your key could read that field on the booking — that is, when it carries guests:read:

  • contact_email, contact_phone, guest_notes — the booking’s own contact details and the note the guest left, inside changes when one of them is edited.
  • submitted_guest_name, submitted_email, submitted_phone — on the reservation.created event, what the booker actually typed, recorded when it differs from the guest profile the booking was matched to.

On reservation.cancelled, cancellation_reason says who ended the booking and how:

  • partner_api is a cancel YOU made through POST /v1/reservations/{id}/cancel.
  • guest_self_service is the guest cancelling from their own manage link.
  • guarantee_expired is a card request that lapsed unpaid.

Anything else was typed by the restaurant; match on the three named values and pass the rest through.

object
key
additional properties
any
modification_token

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.

string
created_at
required

When the booking was recorded: ISO 8601 with the restaurant’s UTC offset.

string format: date-time
updated_at
required

When anything on the booking last changed, in the same format. The value updated_since compares against.

string format: date-time
Example
{
"object": "reservation",
"status": "awaiting_guarantee",
"source": "manual",
"booking_channel": "back_office",
"guest_notifications": "service_managed",
"guest": {
"object": "guest",
"language": "fr",
"source": "manual"
},
"company": {
"object": "company"
},
"tables": [
{
"object": "table",
"section": {
"object": "section",
"area_type": "indoor"
}
}
],
"guarantee": {
"object": "guarantee",
"kind": "imprint",
"state": "awaiting_card",
"origin": "online_booking"
},
"events": [
{
"object": "reservation_event",
"type": "reservation.created"
}
]
}
Service-Version
string

Dated API version applied to this response

The Service-Version header names a version we do not publish

Media typeapplication/json

The envelope every public-API error answers with, whatever the status.

object
error
required

Every error response carries this one object.

object
type
required

The broad class of the error, from the HTTP status.

  • invalid_request_error: a 400, 403, 404, 409 or 422 — something about the request to change.
  • authentication_error: a 401 — the key is missing, invalid, revoked or expired.
  • rate_limit_error: a 429 — slow down and retry.
  • api_error: any other status, a fault on our side.
string
Allowed values: invalid_request_error authentication_error rate_limit_error api_error
code
required

The specific error, stable and machine-readable, e.g. not_found. Branch on this, not on message.

string
message
required

A human-readable explanation for the developer. Its wording can change.

string
param

The request parameter the error is about, when there is one — e.g. expand or limit. Absent — not null — when the error names no field.

string
doc_url
required

A link to this code in the errors reference. Always present.

string
Example
{
"error": {
"type": "invalid_request_error"
}
}
X-Request-Id
string

Unique id for this request. Quote it to support: it is how a specific call is found in our logs. Generated per request, or echoed from the X-Request-Id you sent.

No API key, or not a live one

Media typeapplication/json

The envelope every public-API error answers with, whatever the status.

object
error
required

Every error response carries this one object.

object
type
required

The broad class of the error, from the HTTP status.

  • invalid_request_error: a 400, 403, 404, 409 or 422 — something about the request to change.
  • authentication_error: a 401 — the key is missing, invalid, revoked or expired.
  • rate_limit_error: a 429 — slow down and retry.
  • api_error: any other status, a fault on our side.
string
Allowed values: invalid_request_error authentication_error rate_limit_error api_error
code
required

The specific error, stable and machine-readable, e.g. not_found. Branch on this, not on message.

string
message
required

A human-readable explanation for the developer. Its wording can change.

string
param

The request parameter the error is about, when there is one — e.g. expand or limit. Absent — not null — when the error names no field.

string
doc_url
required

A link to this code in the errors reference. Always present.

string
Example
{
"error": {
"type": "invalid_request_error"
}
}
X-Request-Id
string

Unique id for this request. Quote it to support: it is how a specific call is found in our logs. Generated per request, or echoed from the X-Request-Id you sent.

The API key does not carry the reservations:read scope

Media typeapplication/json

The envelope every public-API error answers with, whatever the status.

object
error
required

Every error response carries this one object.

object
type
required

The broad class of the error, from the HTTP status.

  • invalid_request_error: a 400, 403, 404, 409 or 422 — something about the request to change.
  • authentication_error: a 401 — the key is missing, invalid, revoked or expired.
  • rate_limit_error: a 429 — slow down and retry.
  • api_error: any other status, a fault on our side.
string
Allowed values: invalid_request_error authentication_error rate_limit_error api_error
code
required

The specific error, stable and machine-readable, e.g. not_found. Branch on this, not on message.

string
message
required

A human-readable explanation for the developer. Its wording can change.

string
param

The request parameter the error is about, when there is one — e.g. expand or limit. Absent — not null — when the error names no field.

string
doc_url
required

A link to this code in the errors reference. Always present.

string
Example
{
"error": {
"type": "invalid_request_error"
}
}
X-Request-Id
string

Unique id for this request. Quote it to support: it is how a specific call is found in our logs. Generated per request, or echoed from the X-Request-Id you sent.

No such reservation

Media typeapplication/json

The envelope every public-API error answers with, whatever the status.

object
error
required

Every error response carries this one object.

object
type
required

The broad class of the error, from the HTTP status.

  • invalid_request_error: a 400, 403, 404, 409 or 422 — something about the request to change.
  • authentication_error: a 401 — the key is missing, invalid, revoked or expired.
  • rate_limit_error: a 429 — slow down and retry.
  • api_error: any other status, a fault on our side.
string
Allowed values: invalid_request_error authentication_error rate_limit_error api_error
code
required

The specific error, stable and machine-readable, e.g. not_found. Branch on this, not on message.

string
message
required

A human-readable explanation for the developer. Its wording can change.

string
param

The request parameter the error is about, when there is one — e.g. expand or limit. Absent — not null — when the error names no field.

string
doc_url
required

A link to this code in the errors reference. Always present.

string
Example
{
"error": {
"type": "invalid_request_error"
}
}

Too many requests on the read budget

Media typeapplication/json

The envelope every public-API error answers with, whatever the status.

object
error
required

Every error response carries this one object.

object
type
required

The broad class of the error, from the HTTP status.

  • invalid_request_error: a 400, 403, 404, 409 or 422 — something about the request to change.
  • authentication_error: a 401 — the key is missing, invalid, revoked or expired.
  • rate_limit_error: a 429 — slow down and retry.
  • api_error: any other status, a fault on our side.
string
Allowed values: invalid_request_error authentication_error rate_limit_error api_error
code
required

The specific error, stable and machine-readable, e.g. not_found. Branch on this, not on message.

string
message
required

A human-readable explanation for the developer. Its wording can change.

string
param

The request parameter the error is about, when there is one — e.g. expand or limit. Absent — not null — when the error names no field.

string
doc_url
required

A link to this code in the errors reference. Always present.

string
Example
{
"error": {
"type": "invalid_request_error"
}
}
RateLimit-Limit
integer

Requests permitted in the read burst

RateLimit-Remaining
integer

Requests remaining in the read burst

RateLimit-Reset
integer

Seconds until the read bucket refills

RateLimit-Resource
string

Which budget the numbers describe. read here.

Retry-After
integer

Seconds to wait before retrying

X-Request-Id
string

Unique id for this request. Quote it to support: it is how a specific call is found in our logs. Generated per request, or echoed from the X-Request-Id you sent.