Booking API

Version 1.0.0

Overview

Public API for table bookings: check availability, create a booking, and let the guest view, change or cancel it with the token returned at creation. Venue-side events are delivered as signed webhooks (see the webhooks section).

Base URL
https://api.thaodien.app

Embed the booking form

Paste this where the form should appear, with your venue slug. It resizes to fit.

<script src="https://ingoldcoast.app/embed/book.js" data-venue="your-venue-slug" async></script>

Your page can listen for a window message { source: 'venuos-booking', type: 'booking.created', confirmation_code }. No guest details are sent.

Endpoints

post/bookings

Create a booking

Request body

FieldTypeRequiredDescription
customer_emailstring (email)No
customer_lang"en" | "vi" | "ko"No
customer_namestringYes
customer_phonestringYes
datestring (date)Yes
marketing_consentobjectNoOnly when the guest ticked the box shown from consent-wording. Stored on the booking; it becomes a recorded opt-in only when the guest, signed in with an OTP session, confirms it explicitly.
marketing_consent.accepted"true"No
marketing_consent.channels"sms" | "zalo" | "email"[]No
marketing_consent.wording_versionstringNo
occasionstringNo
party_sizeintegerYes
special_requestsstringNo
timestringYesExample: 19:00
venue_idstring (uuid)Yes

Responses

  • 201Created (CreatedBooking)
  • 400Error (Error)
  • 403Error (Error)
  • 409No longer available. `code` is `table_taken` (no free table) or `pacing_full` (the interval cover cap is reached); a duplicate booking has `duplicate: true`. (Error)
  • 429Error (Error)

201 body

FieldTypeRequiredDescription
confirmation_codestringNo
manage_urlstring (uri)No
modify_tokenstringNo
status"pending" | "confirmed"No
successbooleanNo
get/bookings/manage/{modify_token}

View a booking

Parameters

NameInTypeRequiredDescription
modify_tokenpathstringYesThe modify_token returned when the booking was created. It is the only credential.

Responses

  • 200The booking
  • 404Error (Error)
  • 410Error (Error)
put/bookings/manage/{modify_token}

Change date, time, party size or requests

Parameters

NameInTypeRequiredDescription
modify_tokenpathstringYesThe modify_token returned when the booking was created. It is the only credential.

Request body

FieldTypeRequiredDescription
datestring (date)No
party_sizeintegerNo
special_requestsstringNo
timestringNo
updated_atstring (date-time)NoThe updated_at the page loaded; a mismatch answers 409 conflict_retry.

Responses

  • 200Updated
  • 400Error (Error)
  • 404Error (Error)
  • 409Error (Error)
  • 410Error (Error)
delete/bookings/manage/{modify_token}

Cancel a booking

Parameters

NameInTypeRequiredDescription
modify_tokenpathstringYesThe modify_token returned when the booking was created. It is the only credential.

Responses

  • 200Cancelled
  • 400Error (Error)
  • 404Error (Error)
  • 410Error (Error)
post/bookings/manage/{modify_token}/marketing-consent

Confirm the marketing consent ticked on the booking form

Needs the manage link AND a verified customer session (Bearer token from an OTP login) whose customer is the booking's customer: 401 without a session, 403 for another customer. Body {"action": "confirm"}.

Parameters

NameInTypeRequiredDescription
modify_tokenpathstringYesThe modify_token returned when the booking was created. It is the only credential.

Responses

  • 200Recorded
  • 400Error (Error)
  • 401Error (Error)
  • 403Error (Error)
  • 404Error (Error)
  • 409Nothing pending, or the wording changed
  • 410Error (Error)
get/bookings/venues/{venue_id}/availability

Available slots for a date and party size

Times are venue-local wall-clock. A slot is unavailable when no table fits, the venue is closed, or the per-interval cover cap is reached.

Parameters

NameInTypeRequiredDescription
venue_idpathstring (uuid)Yes
datequerystring (date)Yes
party_sizequeryintegerYes

Responses

  • 200Slots (Availability)
  • 400Error (Error)
  • 403Error (Error)
  • 404Error (Error)

200 body

FieldTypeRequiredDescription
datestring (date)No
party_sizeintegerNo
settingsobjectNo
settings.cancellation_hoursintegerNo
settings.cancellation_policystringNo
settings.deposit_amountnumberNo
settings.requires_depositbooleanNo
slotsobject[]No
slots[].availablebooleanNo
slots[].remaining_capacityintegerNo
slots[].timestringNoExample: 19:00
venue_idstring (uuid)No
venue_namestringNo
get/bookings/venues/{venue_id}/consent-wording

Marketing-consent wording for the booking form

When enabled is true, show the wording next to an unticked checkbox. A ticked box is sent as `marketing_consent` on create. When enabled is false, show nothing.

Parameters

NameInTypeRequiredDescription
venue_idpathstring (uuid)Yes

Responses

  • 200Wording or {enabled: false}
  • 400Error (Error)
post/bookings/venues/{venue_id}/waitlist

Join the waitlist for a full slot

Parameters

NameInTypeRequiredDescription
venue_idpathstring (uuid)Yes

Request body

FieldTypeRequiredDescription
customer_emailstring (email)No
customer_lang"en" | "vi" | "ko"No
customer_namestringYes
customer_phonestringYes
datestring (date)Yes
marketing_consentobjectNoOnly when the guest ticked the box shown from consent-wording. Stored on the booking; it becomes a recorded opt-in only when the guest, signed in with an OTP session, confirms it explicitly.
marketing_consent.accepted"true"No
marketing_consent.channels"sms" | "zalo" | "email"[]No
marketing_consent.wording_versionstringNo
occasionstringNo
party_sizeintegerYes
special_requestsstringNo
timestringYesExample: 19:00
venue_idstring (uuid)Yes

Responses

  • 201Waitlisted
  • 400Error (Error)
  • 403Error (Error)

Webhooks

Events

  • booking.cancelledA booking was cancelled
  • booking.createdA booking was created
  • booking.updatedA booking changed (time, party size, status, table)

Payload

FieldTypeRequiredDescription
createdstring (date-time)No
dataobjectNo
data.booking_idstring (uuid)No
data.cancellation_reasonstring | nullNo
data.cancelled_atstring | nullNo
data.confirmation_codestringNo
data.created_atstringNo
data.customerobjectNo
data.customer.emailstring | nullNo
data.customer.idstring | nullNo
data.customer.namestringNo
data.customer.phonestringNo
data.datestringNo
data.duration_minutesintegerNo
data.kind"booking"No
data.occasionstring | nullNo
data.party_sizeintegerNo
data.sourcestring | nullNo
data.special_requestsstring | nullNo
data.statusstringNo
data.table_idstring | nullNo
data.timestringNo
data.updated_atstringNo
idstring (uuid)No
type"booking.created" | "booking.updated" | "booking.cancelled"No

Signature

Header
X-Venuos-Signature
Format
t=<unix seconds>,v1=<hex>[,v1=<hex>]
Algorithm
HMAC-SHA256 of "<t>.<raw request body>" with the endpoint secret
Tolerance
300 seconds
Retries
Non-2xx or timeout is retried after 1m, 5m, 30m, 2h, 6h, 24h, then marked dead (7 attempts).
Setup
Venue owners add endpoints in the portal under Settings, Data and webhooks.

Verify (Python)

import hmac, hashlib, time
def verify(secret, header, raw_body, tolerance=300):
    parts = [p.split('=', 1) for p in header.split(',') if '=' in p]
    t = int(dict(parts)['t'])
    if abs(time.time() - t) > tolerance:
        return False
    expected = hmac.new(secret.encode(), str(t).encode() + b'.' + raw_body, hashlib.sha256).hexdigest()
    return any(hmac.compare_digest(expected, v) for k, v in parts if k == 'v1')

Errors

Failed requests return a JSON body with these fields.

FieldTypeRequiredDescription
codestringNo
errorstringNo
waitlist_availablebooleanNo
  • 400Error
  • 401Error
  • 403Error
  • 404Error
  • 409No longer available. `code` is `table_taken` (no free table) or `pacing_full` (the interval cover cap is reached); a duplicate booking has `duplicate: true`.
  • 410Error
  • 429Error