Skip to content

List holds

GET
/v1/holds
curl --request GET \
--url https://api.useservice.app/v1/holds \
--header 'Authorization: Bearer <token>'

Every hold the restaurant has that did not become a booking — live, released and expired — newest first. Requires the reservations:read scope.

A hold that CONVERTED is absent, deliberately: it is a reservation now and it is in GET /v1/reservations. Its hold id still resolves on GET /v1/holds/{id}, answering status: "converted" and naming the booking.

Order: by when the hold was taken, newest first (created_at descending) — NOT by service date or seating time.

status
string

One of held, released, expired. converted is a 400 — those are reservations and are not listed here.

date
string format: date

The SERVICE date held, YYYY-MM-DD. For a range use date[gte] and/or date[lte] instead. ?service_date= is not a filter and is a 400 naming it.

date[gte]
string format: date

Service date on or after this day, YYYY-MM-DD. Combine with date[lte].

date[lte]
string format: date

Service date on or before this day, YYYY-MM-DD.

limit
integer

How many objects to return: 1 to 100, default 20. A larger value is lowered to 100 rather than refused; 0, a negative number or anything that is not an integer is a 400.

starting_after
string

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.

ending_before
string

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.

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.

Holds listed

Media typeapplication/json
object
object
required
string
Allowed values: list
data
required
Array<object>

A claim on a slot that occupies capacity without being a booking. It holds a real table, so it counts against availability for as long as it lives.

object
object
required

The object type: always hold.

string
Allowed values: hold
id
required

Opaque hold id (hold_…). A hold and the reservation it becomes are the same row and share this id’s suffix, so hold_AbC converts into resv_AbC.

string
kind
required

basket releases the table the instant expires_at passes; commitment keeps it until the sweep that runs every five minutes picks the hold up. Both lapse on their own; only the minutes between the deadline and the table reopening differ.

string
Allowed values: basket commitment
status
required

Lifecycle of the hold itself, not of a reservation.

  • held: live and occupying a table. The only state a conversion works from.
  • expired: the deadline passed and the table is back on sale. On a basket hold this is reported from the moment expires_at passes, because that kind reopens its table at read time. On a commitment hold the table is held past the deadline until the five-minute sweep resolves it, so the hold reads held for up to five minutes after expires_at and expired from then on. Either way it is the same lapse, and hold.expired announces it.
  • released: handed back deliberately — you called DELETE /v1/holds/{id}, or the restaurant released it from its own board.
  • converted: it became the booking named in reservation.
string
Allowed values: held expired released converted
party_size
required

How many guests the held seating is for.

integer
service_date
required

The service day of the held seating, YYYY-MM-DD in the restaurant’s timezone. A seating after midnight keeps the date its service began on.

string format: date
starts_at
required

Start of the SEATING the hold covers — not the hold deadline. ISO 8601 with the restaurant’s UTC offset.

string format: date-time
ends_at

End of the seating the hold covers, in the same format. null when no seating duration applies.

string format: date-time
nullable
expires_at
required

When the hold lapses. Authoritative: the server sets it, clamping whatever was requested. Null once the hold is over.

string format: date-time
nullable
ended_at
required

When the hold was released or lapsed. Null while it is live, and null once it has converted — a converted hold did not end, it became the booking named in reservation.

string format: date-time
nullable
reservation
required

The booking this hold became (resv_…), or null.

string
nullable
tables
required

The tables the hold occupies, each with its seating area.

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
excluded_from_shift_limits
required

True when this hold does not count against the shift’s covers caps, with the same meaning as on a reservation: they neither charge it nor refuse it. It is carried onto the booking when the hold converts, so converting an exempt hold needs the reservations:write:override scope.

boolean
metadata
required

Your own reconciliation data, echoed verbatim. Carried onto the reservation when the hold converts. ⚠️ Readable by every integration this restaurant has authorised, not only by the one that wrote it: holds are scoped to the restaurant, so another of its API keys can read this hold and this object. Put reconciliation ids here, not commercial terms or anything you would not show a competitor.

object
key
additional properties
any
created_at
required

When the hold was taken: ISO 8601 with the restaurant’s UTC offset.

string format: date-time
updated_at
required

When the hold last changed, in the same format.

string format: date-time
has_more
required
boolean
Example
{
"object": "list",
"data": [
{
"object": "hold",
"kind": "basket",
"status": "held",
"tables": [
{
"object": "table",
"section": {
"object": "section",
"area_type": "indoor"
}
}
]
}
]
}

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.

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.