Reservations
Reservations are table bookings at one of the merchant’s stores. They live in the same table the POS host screen, the back office and the merchant’s own online booking page read, so a booking you create appears on the store’s reservation list straight away, and a status the host changes at the door is what your next read returns.
Every reservation belongs to a customer. Find or create the customer first, then book for them.
A Full API token includes the reservation scopes. If bookings are all your integration does, use the Reservation API instead — it is the same endpoints with a narrower bundle and no product-level gate.
Endpoints
Section titled “Endpoints”| Method | Path | Scope |
|---|---|---|
GET | /v1/reservations | reservations.read |
GET | /v1/reservations/:id | reservations.read |
POST | /v1/reservations | reservations.write |
PATCH | /v1/reservations/:id | reservations.write |
There is no DELETE. To call a booking off, move it to cancelled — the row stays, so the
merchant keeps the history.
A booking from start to finish
Section titled “A booking from start to finish”-
Find or create the customer. Search by mobile first — merchants match guests by phone at the door, and a duplicate splits the guest’s history.
Terminal window curl -s "$API_BASE_URL/customers?mobile=%2B919845012345" \-H "Authorization: Bearer $ACCESS_TOKEN"If
datais empty, create the customer and keep the returnedid. -
Create the reservation for that customer at a store. The response gives you the new reservation id.
-
Move it through its states as your product learns more — confirmed by the guest, seated at the door, completed when they leave, or cancelled.
-
Read it back to show the host stand or reconcile with your own system.
Create a reservation
Section titled “Create a reservation”curl -s "$API_BASE_URL/reservations" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H 'Content-Type: application/json' \ -d '{ "customerId": 5521, "storeId": 1, "reservationDate": "2026-10-24", "reservationTime": "19:30", "partySize": 4, "occasion": "Anniversary", "phone": "+919845012345", "note": "Window table if possible", "preOrderedFoods": [ { "name": "Chocolate cake", "quantity": 1, "price": 18, "notes": "Candle, no message" } ] }'{ "data": { "reservationId": 318 }}The create response carries only the id. Call GET /v1/reservations/:id when you need the
stored row.
Fields
Section titled “Fields”| Field | Type | Required | Notes |
|---|---|---|---|
customerId | number | yes | An existing customer id |
reservationDate | string | yes | YYYY-MM-DD. Must be a real date — 2026-02-30 is rejected |
reservationTime | string | yes | HH:mm or HH:mm:ss, 24-hour, between 00:00 and 23:59:59 |
partySize | number | yes | Number of guests, at least 1 |
storeId | number | see below | The store the table is at. Always send it |
durationMinutes | number | no | 15–600. Defaults to the store’s reservation duration, else 120 |
status | string | no | Defaults to confirmed when the store auto-accepts bookings, else pending. See lifecycle |
occasion | string | no | Free text up to 100 characters — “Birthday”, “Anniversary”, “Business” |
note | string | no | Note for front-of-house staff, up to 500 characters |
phone | string | no | Contact for this booking, up to 20 characters |
email | string | no | Up to 120 characters |
address | string | no | Up to 300 characters |
customerPreferences | string | no | Allergies, seating preferences — up to 500 characters |
customerPhotoUrl | string | no | Photo the host can match at the door, up to 500 characters |
preOrderedFoods | array | no | Dishes ordered with the booking: { name, quantity, price, notes } per line |
extra | object | no | Accepted for compatibility; not stored |
Unknown properties are rejected with 400 — guests instead of partySize fails loudly
rather than booking a table for nobody.
Date and time are the store’s clock
Section titled “Date and time are the store’s clock”reservationDate and reservationTime are the wall-clock time at the store — what the guest
booked and what the host reads — not an instant. Send 19:30 for a 7:30 pm table regardless
of where your servers run, and do not convert to UTC. “Today” and “in the past” are judged on
the store’s clock too.
Contact details
Section titled “Contact details”phone, email and address on the booking are a snapshot for this reservation — useful
for a guest booking on someone else’s behalf. When you leave them out, reads fall back to the
customer record’s mobile, email and address.
Booking rules
Section titled “Booking rules”Merchants can set reservation rules per store in the back office. A rule the merchant has not set is not enforced — with no rules at all, any number of overlapping bookings is allowed.
| Rule | Checked | Error | Status |
|---|---|---|---|
| Reservations switched off for the store | create | reservations.disabled | 400 |
| Date before the store’s today | create | reservations.date_in_past | 400 |
| Minimum party size | create, or an update that changes partySize | reservations.party_too_small | 400 |
| Maximum party size | create, or an update that changes partySize | reservations.party_too_large | 400 |
| How many days ahead a booking may be made | create, or an update that moves the date or time | reservations.too_far_ahead | 400 |
| Minimum notice before the booking starts | create, or an update that moves the date or time | reservations.notice_too_short | 400 |
| Maximum bookings per day | when the booking holds a table | reservations.day_full | 409 |
| Maximum overlapping bookings | when the booking holds a table | reservations.slot_full | 409 |
| Seat capacity across overlapping bookings | when the booking holds a table | reservations.seats_full | 409 |
| The same customer already has a booking at that exact date and time, at any store | create, or an update that moves the booking | reservations.customer_already_has_reservation | 400 |
pending, confirmed and seated bookings hold a table; completed and cancelled ones do
not. Two bookings overlap when their time windows — start plus duration, widened by any
buffer the merchant set before and after — intersect. Bookings that merely touch, one ending
at 21:30 and the next starting at 21:30, do not overlap.
The limit that was hit comes back in params, so you can explain it to the guest:
{ "status": "error", "statusCode": 400, "timestamp": "2026-10-15T06:05:21.402Z", "path": "/v1/reservations", "message": "Party size cannot be more than 20.", "code": "reservations.party_too_large", "params": { "max": 20 }, "correlationId": "c1d7a0e2-5b4f-4f0e-9a51-0f6b2a8c4e19"}| Code | params |
|---|---|
reservations.date_in_past | date |
reservations.party_too_small | min |
reservations.party_too_large | max |
reservations.too_far_ahead | days |
reservations.notice_too_short | minutes |
reservations.day_full, reservations.slot_full | max |
reservations.seats_full | capacity, available |
Status lifecycle
Section titled “Status lifecycle”| From | Allowed next states |
|---|---|
pending | confirmed, cancelled |
confirmed | seated, completed, cancelled |
seated | completed, cancelled |
completed | — |
cancelled | — |
completed and cancelled are final: nothing moves out of them, and a completed booking
cannot be cancelled. Sending the status a booking already has is accepted. Any other move is
refused:
{ "status": "error", "statusCode": 409, "timestamp": "2026-10-15T06:05:05.982Z", "path": "/v1/reservations/318", "message": "A seated reservation cannot be changed to pending.", "code": "reservations.invalid_status_transition", "params": { "from": "seated", "to": "pending" }, "correlationId": "57cc9574-36d2-43d9-a55a-14c4bf1ec80a"}The host can change a status at the store at any time. Read the booking before you act on
its state, and treat reservations.invalid_status_transition as “someone got there first”,
not as a bug in your request.
Update a reservation
Section titled “Update a reservation”curl -s -X PATCH "$API_BASE_URL/reservations/318" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H 'Content-Type: application/json' \ -d '{ "status": "confirmed" }'{ "data": { "reservationId": 318 }}PATCH takes the same fields as create, all optional. Send only what changes — fields you
leave out are untouched, and null clears a text field such as note.
Moving a booking that holds a table — a new date, time, duration, party size, store or
customer — re-runs the booking rules against the store’s other bookings.
The window rules (days ahead, minimum notice) run only when the date or time moves, and the
party-size rules only when partySize changes, so a booking made before the merchant
tightened a limit can still have its note edited.
curl -s -X PATCH "$API_BASE_URL/reservations/318" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H 'Content-Type: application/json' \ -d '{ "reservationTime": "20:00", "partySize": 5 }'Read reservations
Section titled “Read reservations”One reservation
Section titled “One reservation”curl -s "$API_BASE_URL/reservations/318" \ -H "Authorization: Bearer $ACCESS_TOKEN"{ "data": { "ID": 318, "CustomerId": 5521, "CustomerName": "Priya Nair", "StoreId": 1, "StoreName": "Brigade Road", "ReservationDate": "2026-10-24", "ReservationTime": "19:30:00", "PartySize": 4, "DrationMinutes": 120, "Status": "pending", "Occasion": "Anniversary", "Note": "Window table if possible", "Phone": "+919845012345", "Email": "priya@example.com", "Address": null, "CustomerPreferences": null, "CustomerPhotoUrl": null, "PreOrderedFoods": [ { "name": "Chocolate cake", "quantity": 1, "price": 18, "notes": "Candle, no message" } ], "Channel": 0, "ChannelReservationId": null, "Source": null, "CreatedBy": 1, "UpdatedBy": null, "EntryDate": "2026-10-15 11:34:47", "LastUpdate": "2026-10-15 11:34:47" }}| Field | Notes |
|---|---|
ID | Reservation id |
CustomerId, CustomerName | The customer, with their name joined in |
StoreId, StoreName | The store, with its name joined in. StoreId is null on a booking made without a store |
ReservationDate, ReservationTime | Store wall clock, YYYY-MM-DD and HH:mm:ss |
PartySize | Number of guests |
DrationMinutes | Duration in minutes |
Status | pending, confirmed, seated, completed or cancelled |
Occasion, Note, CustomerPreferences, CustomerPhotoUrl | As sent on create or update |
Phone, Email, Address | The booking’s own contact details, else the customer record’s |
PreOrderedFoods | Always an array; [] when nothing was pre-ordered |
Source | online for a guest who booked through the merchant’s own online booking page, quiqqy for a booking from the QUIQQY channel, else null |
Channel, ChannelReservationId | The external channel and its booking id, for channel bookings |
CreatedBy, UpdatedBy | Merchant employee ids. Single-reservation reads only |
EntryDate, LastUpdate | YYYY-MM-DD HH:mm:ss |
Listing
Section titled “Listing”curl -s "$API_BASE_URL/reservations?storeId=1&fromDate=2026-10-24&toDate=2026-10-24&order=asc" \ -H "Authorization: Bearer $ACCESS_TOKEN"{ "data": [ /* reservation rows, as above */ ], "meta": { "page": 1, "limit": 10, "total": 7 }}| Parameter | Type | Notes |
|---|---|---|
storeId | number | One store |
status | string | pending, confirmed, seated, completed or cancelled |
customerId | number | Bookings of one customer |
fromDate / toDate | string | Reservation date window, YYYY-MM-DD, both ends inclusive |
search | string | Contains-match on the customer’s name, mobile and email, and the booking’s phone and email |
order | asc | desc | By reservation date, then time. Default desc |
page, limit | number | Standard paging; limit defaults to 10, maximum 100 |
The list is always ordered by reservation date and time — sort is not accepted on this
endpoint and returns 400. List rows carry everything a single read does except CreatedBy
and UpdatedBy, so a host screen needs one call, not one per booking.
Errors
Section titled “Errors”| Code | Status | Meaning | Fix |
|---|---|---|---|
reservations.not_found | 404 | No reservation with that id in this merchant | Check the id returned at creation |
reservations.invalid_status_transition | 409 | The lifecycle does not allow that move | Read the booking; it may have moved at the store |
reservations.customer_already_has_reservation | 400 | The customer already has a booking at that exact date and time | Update the existing booking instead |
reservations.disabled | 400 | The merchant switched reservations off for the store | Do not offer booking at that store |
reservations.date_in_past | 400 | The date is before the store’s today | Send a date on or after today, store time |
reservations.party_too_small, reservations.party_too_large | 400 | Outside the store’s party-size limits | Show params.min / params.max to the guest |
reservations.too_far_ahead | 400 | Beyond the store’s booking horizon | Show params.days |
reservations.notice_too_short | 400 | Too close to the start time | Show params.minutes |
reservations.day_full, reservations.slot_full, reservations.seats_full | 409 | The store is fully booked for that day or time | Offer another time |
reservations.busy | 409 | Another booking for the same store was being saved at that moment | Retry once after a short pause |
| — | 503 | Reservations are not set up for this merchant | Contact the merchant |
Validation failures — a missing required field, 2026-02-30, 25:00, a duration under 15,
an unknown property — are 400 with no code; the message names the fields. See
errors.