List reservations
const url = 'https://api.useservice.app/v1/reservations?status=awaiting_guarantee&source=manual';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://api.useservice.app/v1/reservations?status=awaiting_guarantee&source=manual' \ --header 'Authorization: Bearer <token>'Lists the restaurant’s reservations. Requires the reservations:read scope.
Guest data. A booking’s contact_email, contact_phone and guest_notes are
included only when your key also carries guests:read — on every booking alike,
including the ones your key made. Otherwise the three fields are absent, not null.
What is and is not returned. Bookings the restaurant never had are excluded: a
guest part-way through the booking widget’s card step, and a waitlist offer nobody has
claimed, are not reservations yet and never appear — neither while they are live nor
as the cancelled rows they become if abandoned. One kind of unfinished booking IS
returned: a card request, status awaiting_guarantee, where the restaurant or an API
client booked the table and the guest has been emailed a payment link. Those are real
bookings holding a real table, they appear from the moment they are created, and
GET /v1/reservations/{id} resolves for them. They resolve to confirmed or
cancelled within about 48 hours, so treat awaiting_guarantee as not-yet-booked.
They can be read before reservation.created is delivered, because that webhook fires when
the guest saves the card. If you reconcile webhooks against this endpoint, expect a booking
to appear here first.
Order: by service date, latest first (service_date descending). Within one service
date, bookings come in the order they were recorded, newest first — NOT by start time, so
for a seating-order view of an evening, sort the page by starts_at yourself.
With updated_since the order becomes updated_at ascending, oldest change first (ties in
the order the bookings were recorded), so a sync that pages forward to has_more: false sees
every change. A booking that changes again while you page moves to the end, where you will
meet it again.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Query Parameters
Section titled “Query Parameters”Filter by status. One value only — a comma-separated list is not a status and is
a 400 naming it, as is any value outside the list above (late is an internal
marker and is not accepted).
Three of the accepted values are narrower than they look, because this collection
never serves an unfinalized hold: awaiting_guest and held match nothing at
all, and awaiting_guarantee matches card requests only, never a booking
part-way through the widget’s card step.
Filter by the mechanism the booking came through (see booking_channel for the channel it arrived on)
Filter by guest public id (gst_…)
The service date, YYYY-MM-DD in the restaurant’s timezone — the day the booking is FOR (service_date on each row), not the day it was made. For a range use date[gte] and/or date[lte] instead. ?service_date= is not a filter and is a 400 naming it.
Service date on or after this day, YYYY-MM-DD. Combine with date[lte].
Service date on or before this day, YYYY-MM-DD.
An ISO 8601 timestamp; one without an offset is read as UTC. Returns only bookings whose updated_at is at or after it, and changes the order to updated_at ascending — see Order: above.
How many objects to return: 1 to 100, default 20. A larger value is lowered to 100 (25 when expand is passed) rather than refused; 0, a negative number or anything that is not an integer is a 400.
The id of an object in this list — normally the last one on the page you have. Returns the objects that come after it, in the order the operation description gives. has_more: false means you have reached the end. An id this list cannot find is a 400.
The id of an object in this list — normally the first one on the page you have. Returns the objects that come just before it, walking the same order backwards; the page itself still reads in list order. Ignored when starting_after is also sent. An id this list cannot find is a 400.
Batch fetch by comma-separated public ids (resv_…), max 100
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.
Header Parameters
Section titled “Header Parameters”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.
Responses
Section titled “Responses”Reservations listed
object
object
The object type: always reservation.
The booking’s id, resv_…. Opaque and permanent. A booking converted from a hold keeps that hold’s suffix: hold_AbC becomes resv_AbC.
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.
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).
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.
How many guests the booking is for.
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.
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.
When the table is expected back, in the same format as starts_at. null when no seating duration applies to the booking.
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.
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.
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.
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.
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.
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.
The guest’s id, gst_… — the unexpanded form.
A guest profile from the restaurant’s guest book. Guest data: served by GET /v1/guests and ?expand=guest, both of which need the guests:read scope, and carried by every guest.* webhook delivery.
object
The object type: always guest.
The guest’s id, gst_…. Opaque and permanent. When two profiles are merged, the one that disappears keeps resolving to the survivor in ?id= on GET /v1/guests.
First name. A profile has a first name or a last name, possibly both; null when it has only the other.
Last name. null when the profile has only a first name.
Primary e-mail address, trimmed and lower-cased. null when none is known.
Primary phone number: E.164 (+33612345678) when it could be parsed for the restaurant’s country, otherwise as entered with separators removed — so do not assume E.164. null when none is known.
The restaurant’s own notes on the guest profile: free text written by its staff, or carried over from its previous reservation system — the restaurant’s words about the guest, not the guest’s. A booking’s guest_notes are the guest’s own. null when there are none.
Whether the restaurant has flagged this guest as unwelcome. A flag only: it does not by itself refuse a booking.
Why the guest was flagged — required by the restaurant when it sets blacklisted. null when the guest is not flagged.
Dietary preferences the restaurant recorded, as free-form labels. [] when none.
Allergies the restaurant recorded, as free-form labels. [] when none.
Date of birth, YYYY-MM-DD. null when not recorded.
An anniversary date the restaurant recorded, YYYY-MM-DD. null when not recorded.
Whether the restaurant marked the guest as a VIP.
The language the restaurant writes to this guest in, as an ISO 639-1 code. Set to the restaurant’s primary language when the profile is created unless one is given, so null only on a profile that never had one.
How the restaurant first learned of this guest. Not the same vocabulary as a
reservation’s source.
manual: entered by the restaurant’s staff.widget: first seen booking in the restaurant’s booking widget.import: brought over from the restaurant’s previous reservation system.host_assertion: first seen booking through the widget embedded on a partner’s page, with the partner vouching for their identity.api: first seen on a booking created through this API.website_newsletter: first seen signing up to the newsletter on the restaurant’s website.
Whether the guest agreed to receive marketing e-mail from the restaurant.
When marketing_email_consent last became true, ISO 8601 with the restaurant’s UTC offset. null while consent is false.
Whether the guest agreed to receive marketing text messages from the restaurant.
When marketing_sms_consent last became true, ISO 8601 with the restaurant’s UTC offset. null while consent is false.
How many of the guest’s bookings ended with them seated (seated, partially_seated or completed). Recomputed hourly, so it can trail a visit by up to about two hours.
How many of the guest’s bookings ended no_show. Recomputed like visit_count.
How many of the guest’s bookings were cancelled, counting only bookings whose time has passed — a cancelled booking for next month is not counted until then. Recomputed like visit_count.
When the guest’s earliest visit (a booking counted in visit_count) started: ISO 8601 with the restaurant’s UTC offset, on the calendar day it actually started — a seating after midnight carries the next day’s date. null until they have one. Recomputed like visit_count.
When the guest’s latest visit started, in the same format as first_visit_at. null until they have one. Recomputed like visit_count.
When the profile was created: ISO 8601 with the restaurant’s UTC offset.
When the profile last changed, in the same format. The value updated_since compares against.
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
The object type: always company.
The company’s id, cmp_….
The company’s name, as the restaurant recorded it.
The tables the booking is seated at, each with its seating area. [] while no table is assigned.
object
The object type: always table.
The table’s id, tbl_…. Opaque and stable.
The table’s name on the restaurant’s floor plan.
The smallest party the table is meant for. Always a number, never null.
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.
The seating area the table is in.
object
The object type: always section.
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.
The area’s name, in the restaurant’s primary_language.
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.
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.
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.
The card guarantee attached to the booking. null when it has none. Always included; no scope or expand needed.
object
The object type: always guarantee.
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.
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, untilexpires_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 fullamountwas charged.partially_charged: less thanamountwas 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.
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.
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.
ISO 4217 currency code of every amount on this object, e.g. EUR.
How much has been charged so far, in minor units of currency. 0 when nothing has.
How much of charged_amount has been refunded, in minor units of currency. 0 when nothing has.
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
The card network, as the payment processor reports it, e.g. visa.
The last four digits of the card number.
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.
While state is awaiting_card, the deadline for the guest to save a card, in the same format. null in every other state.
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.
When the guarantee was created, in the same format.
When the guarantee last changed, in the same format.
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
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.
object
The object type: always reservation_event.
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.
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_reasonsays how.reservation.no_show: the party did not come.
When it happened: ISO 8601 with the restaurant’s UTC offset.
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, insidechangeswhen one of them is edited.submitted_guest_name,submitted_email,submitted_phone— on thereservation.createdevent, 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_apiis a cancel YOU made throughPOST /v1/reservations/{id}/cancel.guest_self_serviceis the guest cancelling from their own manage link.guarantee_expiredis 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
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.
When the booking was recorded: ISO 8601 with the restaurant’s UTC offset.
When anything on the booking last changed, in the same format. The value updated_since compares against.
Example
{ "object": "list", "data": [ { "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" } ] } ]}Headers
Section titled “Headers”Dated API version applied to this response
Invalid request parameter
The envelope every public-API error answers with, whatever the status.
object
Every error response carries this one object.
object
The broad class of the error, from the HTTP status.
invalid_request_error: a400,403,404,409or422— something about the request to change.authentication_error: a401— the key is missing, invalid, revoked or expired.rate_limit_error: a429— slow down and retry.api_error: any other status, a fault on our side.
The specific error, stable and machine-readable, e.g. not_found. Branch on this, not on message.
A human-readable explanation for the developer. Its wording can change.
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.
A link to this code in the errors reference. Always present.
Example
{ "error": { "type": "invalid_request_error" }}Missing or invalid API key
The envelope every public-API error answers with, whatever the status.
object
Every error response carries this one object.
object
The broad class of the error, from the HTTP status.
invalid_request_error: a400,403,404,409or422— something about the request to change.authentication_error: a401— the key is missing, invalid, revoked or expired.rate_limit_error: a429— slow down and retry.api_error: any other status, a fault on our side.
The specific error, stable and machine-readable, e.g. not_found. Branch on this, not on message.
A human-readable explanation for the developer. Its wording can change.
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.
A link to this code in the errors reference. Always present.
Example
{ "error": { "type": "invalid_request_error" }}API key lacks the reservations:read scope
The envelope every public-API error answers with, whatever the status.
object
Every error response carries this one object.
object
The broad class of the error, from the HTTP status.
invalid_request_error: a400,403,404,409or422— something about the request to change.authentication_error: a401— the key is missing, invalid, revoked or expired.rate_limit_error: a429— slow down and retry.api_error: any other status, a fault on our side.
The specific error, stable and machine-readable, e.g. not_found. Branch on this, not on message.
A human-readable explanation for the developer. Its wording can change.
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.
A link to this code in the errors reference. Always present.
Example
{ "error": { "type": "invalid_request_error" }}Rate limit exceeded
The envelope every public-API error answers with, whatever the status.
object
Every error response carries this one object.
object
The broad class of the error, from the HTTP status.
invalid_request_error: a400,403,404,409or422— something about the request to change.authentication_error: a401— the key is missing, invalid, revoked or expired.rate_limit_error: a429— slow down and retry.api_error: any other status, a fault on our side.
The specific error, stable and machine-readable, e.g. not_found. Branch on this, not on message.
A human-readable explanation for the developer. Its wording can change.
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.
A link to this code in the errors reference. Always present.
Example
{ "error": { "type": "invalid_request_error" }}Headers
Section titled “Headers”Requests permitted in the window
Requests remaining in the window
Seconds until the bucket refills
Which budget the numbers above describe: read, write, or ip for the per-IP net
Seconds to wait before retrying