Venue Booking API reference

Generated from the OpenAPI 3.1 document at /api/openapi.json. Server: https://saalo.ee. The Estonian guide with recipes is at /docs/api.

Three surfaces: - **Public** (`/api/v1/{venue}/*`): no authentication, CORS `*`, per-IP rate limits. Powers the booking widget and the venue's own website. Never returns customer data. - **REST** (`/api/v1/rest/*`): read-only, `Authorization: Bearer vb_live_…` (test keys `vb_test_…`). Create keys under Admin > Integrations; available on the Venue and Pro plans. 60 requests per minute per key (`X-RateLimit-*` headers, `Retry-After` on 429). Lists are newest first with cursor pagination (`{data, next_cursor}`). - **Webhooks**: signed HTTPS POSTs to your URL (Pro plan), see the Webhooks tag. Public error bodies are `{error}`, `{error, problems}` or `{problems}`; REST errors are `{error, message}`.

Public

Unauthenticated endpoints for booking forms and websites.

get/api/v1/{venue}/catalog

Venue catalogue (getCatalog)

Everything the booking form needs: venue settings, active rooms, booking types, event types, extras and packages, plus which AI helpers are on. Prices are omitted when the venue hides them. Cached for 60 s. Rate limit: 120 requests per minute per IP.

Parameters

NameInTypeReq.Description
venuepathstringyesVenue slug, e.g. `uks-maja`.
langquery"et" | "en" | "ru" | "lv" | "lt"Return venue-entered names in this language where the venue has translated them.

Responses

  • 200 The catalogue.
    PropertyTypeReq.Description
    venueobjectyes
    venue.namestringyes
    venue.slugstringyes
    venue.localestringyes
    venue.currencystringyes
    venue.vatRatenumberyes
    venue.pricesIncludeVatbooleanyes
    venue.showPricesbooleanyes
    venue.startStepMinutesintegeryes
    venue.minBookingMinutesintegeryes
    venue.maxBookingMinutesintegeryes
    venue.timezonestringyes
    venue.brandobjectyesColours and logo for embedding.
    roomsRoom[]yes
    rooms[].idstring (uuid)yes
    rooms[].namestringyes
    rooms[].descriptionstring | nullyes
    rooms[].capacityintegeryes
    rooms[].areaM2number | nullyes
    rooms[].equipmentanyyesEquipment list as entered by the venue.
    rooms[].restrictionsstring | nullyes
    rooms[].openHoursobjectyesOpening hours per weekday.
    rooms[].layoutsobject[]yes
    rooms[].layouts[].stylestringyes
    rooms[].layouts[].capacityintegeryes
    bookingTypesobject[]yes
    bookingTypes[].idstring (uuid)yes
    bookingTypes[].codestringyes
    bookingTypes[].namestringyes
    bookingTypes[].descriptionstring | nullyes
    bookingTypes[].termsstring | nullyes
    bookingTypes[].collectsCompanybooleanyes
    bookingTypes[].allowsPublicEventbooleanyes
    eventTypesobject[]yes
    eventTypes[].idstring (uuid)yes
    eventTypes[].namestringyes
    eventTypes[].bookingTypeIdsarray | nullyes
    extrasobject[]yes
    extras[].idstring (uuid)yes
    extras[].namestringyes
    extras[].descriptionnullyes
    extras[].unitstringyes
    extras[].amountCentsinteger | nullyes
    extras[].bookingTypeIdsarray | nullyes
    extras[].minQuantityinteger | nullyes
    packagesobject[]yes
    packages[].idstring (uuid)yes
    packages[].namestringyes
    packages[].descriptionstring | nullyes
    packages[].bookingTypeIdsarray | nullyes
    packages[].roomIdsarray | nullyes
    packages[].wholeHousebooleanyes
    packages[].perPersonCentsinteger | nullyes
    packages[].minGuestsinteger | nullyes
    packages[].maxMinutesinteger | nullyes
    packages[].includedExtraIdsarray | nullyes
    aiobjectyes
    ai.describebooleanyes
    ai.faqbooleanyes
  • 404 Unknown venue, or the feature is switched off (`feature_off`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 410 The venue is being closed (`venue_closed`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 429 Per-IP rate limit exceeded.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.

Example

curl "https://saalo.ee/api/v1/uks-maja/catalog"

get/api/v1/{venue}/rooms

Active rooms (listRooms)

The `rooms` part of the catalogue. Rate limit: 120 requests per minute per IP.

Parameters

NameInTypeReq.Description
venuepathstringyesVenue slug, e.g. `uks-maja`.
langquery"et" | "en" | "ru" | "lv" | "lt"Return venue-entered names in this language where the venue has translated them.

Responses

  • 200 Rooms.
    PropertyTypeReq.Description
    roomsRoom[]yes
    rooms[].idstring (uuid)yes
    rooms[].namestringyes
    rooms[].descriptionstring | nullyes
    rooms[].capacityintegeryes
    rooms[].areaM2number | nullyes
    rooms[].equipmentanyyesEquipment list as entered by the venue.
    rooms[].restrictionsstring | nullyes
    rooms[].openHoursobjectyesOpening hours per weekday.
    rooms[].layoutsobject[]yes
    rooms[].layouts[].stylestringyes
    rooms[].layouts[].capacityintegeryes
  • 404 Unknown venue, or the feature is switched off (`feature_off`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 410 The venue is being closed (`venue_closed`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 429 Per-IP rate limit exceeded.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.

Example

curl "https://saalo.ee/api/v1/uks-maja/rooms"

get/api/v1/{venue}/availability

Free start times for one day (getAvailability)

Free start times per room for one date and duration. `commonStarts` are the starts where every chosen room is free; `takenStarts` are valid starts that are taken in at least one room (the form offers the waitlist for them). Provide `rooms` or `wholeHouse=1`. Rate limit: 120 requests per minute per IP.

Parameters

NameInTypeReq.Description
venuepathstringyesVenue slug, e.g. `uks-maja`.
datequerystring (/^\d{4}-\d{2}-\d{2}$/)yesLocal date, `YYYY-MM-DD`.
durationqueryinteger (min 15, max 20160)yesLength in minutes, 15 to 20160.
roomsquerystringComma-separated room ids. Required unless `wholeHouse=1`.
wholeHousequery"0" | "1"`1` = all active rooms.
eventTypeIdquerystringEvent type id (can affect conflicts).

Responses

  • 200 Availability.
    PropertyTypeReq.Description
    datestring (date)yes
    roomsobject[]yes
    rooms[].roomIdstring (uuid)yes
    rooms[].freeStartsstring (/^\d{2}:\d{2}$/)[]yes
    rooms[].takenStartsstring (/^\d{2}:\d{2}$/)[]yes
    commonStartsstring (/^\d{2}:\d{2}$/)[]yes
    takenStartsstring (/^\d{2}:\d{2}$/)[]yes
  • 400 Validation failed.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 404 Unknown venue, or the feature is switched off (`feature_off`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 410 The venue is being closed (`venue_closed`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 429 Per-IP rate limit exceeded.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.

Example

curl "https://saalo.ee/api/v1/uks-maja/availability?date=2026-11-20&duration=…"

get/api/v1/{venue}/availability/month

Day statuses for a month (getMonthAvailability)

One status per day of the month for the chosen rooms: `free`, `partial`, `full` or `closed`. The month must be within the booking horizon (about 18 months ahead). Rate limit: 120 requests per minute per IP.

Parameters

NameInTypeReq.Description
venuepathstringyesVenue slug, e.g. `uks-maja`.
monthquerystring (/^\d{4}-(0[1-9]|1[0-2])$/)yes`YYYY-MM`.
roomIdsquerystringComma-separated room ids. Required unless `wholeHouse=1`.
wholeHousequery"0" | "1"`1` = all active rooms.
durationMinutesqueryinteger (min 15, max 20160)yesLength in minutes, 15 to 20160.
bookingTypeIdquerystringBooking type id (accepted, currently informational).
eventTypeIdquerystringEvent type id (can affect conflicts).

Responses

  • 200 Month calendar.
    PropertyTypeReq.Description
    monthstring (/^\d{4}-\d{2}$/)yes
    daysobject[]yes
    days[].datestring (date)yes
    days[].status"free" | "partial" | "full" | "closed"yes
  • 400 Validation failed.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 404 Unknown venue, or the feature is switched off (`feature_off`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 410 The venue is being closed (`venue_closed`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 429 Per-IP rate limit exceeded.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.

Example

curl "https://saalo.ee/api/v1/uks-maja/availability/month?month=2026-11&durationMinutes=…"

post/api/v1/{venue}/estimate

Price estimate and availability check (estimatePrice)

Same body as `createRequest` without `customer`. Nothing is stored. Prices come from the pricing engine only; when the venue hides prices the amounts are `null`/empty. `needsManualPrice: true` means the final price comes with the quote. Validation problems answer 400 with `available: false`. Rate limit: 60 requests per minute per IP.

Parameters

NameInTypeReq.Description
venuepathstringyesVenue slug, e.g. `uks-maja`.
langquery"et" | "en" | "ru" | "lv" | "lt"Return venue-entered names in this language where the venue has translated them.

Request body (application/json)

PropertyTypeReq.Description
bookingTypeIdstring (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)yes
eventTypeIdany
datestring (/^\d{4}-\d{2}-\d{2}$/)yes
endDateany
startTimestring (/^([01]\d|2[0-3]):[0-5]\d$/)yes
endTimestring (/^([01]\d|2[0-3]):[0-5]\d$|^24:00$/)yes
roomIdsstring (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)[]
wholeHouseboolean
guestsinteger (min 1, max 10000)yes
extrasobject[]
extras[].extraIdstring (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)yes
extras[].quantityinteger (min 1, max 1000)
packageIdany
accommodationany
layoutany
localeany
titleany
descriptionany
isPublicEventboolean
messageany
leadany

Responses

  • 200 Estimate.
    PropertyTypeReq.Description
    availablebooleanyes
    problemsProblem[]yes
    problems[].codestringyesStable machine-readable code, e.g. `invalid`, `not_available`, `not_taken`, `venue_closed`.
    problems[].messagestringyesHuman-readable, in the customer's language.
    problems[].fieldstringOffending field, when there is one.
    linesobject[]yes
    lines[].labelstringyes
    lines[].quantitynumberyes
    lines[].unitstringyes
    lines[].unitPriceCentsintegeryes
    lines[].totalCentsintegeryes
    netCentsinteger | nullyes
    vatCentsinteger | nullyes
    grossCentsinteger | nullyes
    needsManualPricebooleanyes
    flagsobject[]yes
    flags[].codestringyes
    flags[].messagestringyes
  • 400 Validation failed (`available` is false).
    PropertyTypeReq.Description
    availablebooleanyes
    problemsProblem[]yes
    problems[].codestringyesStable machine-readable code, e.g. `invalid`, `not_available`, `not_taken`, `venue_closed`.
    problems[].messagestringyesHuman-readable, in the customer's language.
    problems[].fieldstringOffending field, when there is one.
    linesobject[]yes
    lines[].labelstringyes
    lines[].quantitynumberyes
    lines[].unitstringyes
    lines[].unitPriceCentsintegeryes
    lines[].totalCentsintegeryes
    netCentsinteger | nullyes
    vatCentsinteger | nullyes
    grossCentsinteger | nullyes
    needsManualPricebooleanyes
    flagsobject[]yes
    flags[].codestringyes
    flags[].messagestringyes
  • 404 Unknown venue, or the feature is switched off (`feature_off`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 410 The venue is being closed (`venue_closed`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 429 Per-IP rate limit exceeded.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.

Example

curl -X POST "https://saalo.ee/api/v1/uks-maja/estimate" \
  -H "Content-Type: application/json" \
  -d '{
    "bookingTypeId": "…",
    "date": "2026-11-20",
    "startTime": "18:00",
    "endTime": "22:00",
    "guests": 1
  }'

post/api/v1/{venue}/requests

Submit a booking request (createRequest)

Creates a booking request; staff review it and send a quote, unless the booking type is set to confirm instantly (or the signed-in customer is pre-approved) and the request is fully priced and free: then the response has `status`. `?source=widget` marks the source as the widget (default `api`). Problems are worded in `locale` (body), else `?lang=`, else the venue's language. A request that trips the spam trap answers 201 with `bookingNumber: null`. Rate limit: 10 requests per minute per IP.

Parameters

NameInTypeReq.Description
venuepathstringyesVenue slug, e.g. `uks-maja`.
langquery"et" | "en" | "ru" | "lv" | "lt"Return venue-entered names in this language where the venue has translated them.
sourcequery"widget" | "api"`widget` for the embeddable widget; anything else is recorded as `api`.

Request body (application/json)

PropertyTypeReq.Description
bookingTypeIdstring (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)yes
eventTypeIdany
datestring (/^\d{4}-\d{2}-\d{2}$/)yes
endDateany
startTimestring (/^([01]\d|2[0-3]):[0-5]\d$/)yes
endTimestring (/^([01]\d|2[0-3]):[0-5]\d$|^24:00$/)yes
roomIdsstring (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)[]
wholeHouseboolean
guestsinteger (min 1, max 10000)yes
extrasobject[]
extras[].extraIdstring (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)yes
extras[].quantityinteger (min 1, max 1000)
packageIdany
accommodationany
layoutany
localeany
titleany
descriptionany
isPublicEventboolean
messageany
leadany
customerobjectyes
customer.namestring (min length 2, max length 200)yes
customer.emailstring (email, /^(?:[A-Za-z0-9_'+\-]+\.)*[A-Za-z0-9_'+\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/, max length 200)yes
customer.phoneany
customer.companyany
customer.regCodeany
customer.newsletterConsentboolean
website_urlstringHoneypot: leave empty. A filled value is silently dropped.
form_stampstringSigned render time of the venue's form. API clients omit it.

Responses

  • 201 Request created.
    PropertyTypeReq.Description
    bookingNumberstring | nullyesBooking number, e.g. `UM-2026-0042`.
    estimateEstimate | nullyesSame shape as the `estimatePrice` response; `null` when dropped as spam.
    quoteUrlstringOnly present when the venue sends instant quotes with a visible link.
    status"confirmed" | "awaiting_payment" | "awaiting_staff"Only present for an instant confirmation: `confirmed`, `awaiting_payment` (deposit link in `paymentUrl`) or `awaiting_staff`.
    paymentUrlstringDeposit payment link, with `status: awaiting_payment`.
  • 400 Validation failed.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 404 Unknown venue, or the feature is switched off (`feature_off`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 409 The chosen time is not available.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 410 The venue is being closed (`venue_closed`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 429 Per-IP rate limit exceeded.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.

Example

curl -X POST "https://saalo.ee/api/v1/uks-maja/requests" \
  -H "Content-Type: application/json" \
  -d '{
    "bookingTypeId": "…",
    "date": "2026-11-20",
    "startTime": "18:00",
    "endTime": "22:00",
    "guests": 1,
    "customer": {
      "name": "Mari Maasikas",
      "email": "mari@example.ee"
    }
  }'

post/api/v1/{venue}/requests/series

Repeat a booking weekly or every second week (signed-in customer) (createRequestSeries)

Body is the booking request (without `customer`: the signed-in portal customer from the session cookie is used) plus `repeat: { intervalWeeks: 1|2, endsOn }` (2 to 26 dates, the weekday of `date`). With `preview: true` nothing is saved and `dates` lists every date with its clash. Otherwise one booking is created per free date: confirmed at once for a pre-approved customer when the instant-confirm rules hold for that date, else a request for staff. 401 `login_required` without a portal session. Rate limit: 10 requests per minute per IP.

Parameters

NameInTypeReq.Description
venuepathstringyesVenue slug, e.g. `uks-maja`.
langquery"et" | "en" | "ru" | "lv" | "lt"Return venue-entered names in this language where the venue has translated them.

Request body (application/json)

PropertyTypeReq.Description
bookingTypeIdstring (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)yes
eventTypeIdany
datestring (/^\d{4}-\d{2}-\d{2}$/)yes
endDateany
startTimestring (/^([01]\d|2[0-3]):[0-5]\d$/)yes
endTimestring (/^([01]\d|2[0-3]):[0-5]\d$|^24:00$/)yes
roomIdsstring (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)[]
wholeHouseboolean
guestsinteger (min 1, max 10000)yes
extrasobject[]
extras[].extraIdstring (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)yes
extras[].quantityinteger (min 1, max 1000)
packageIdany
accommodationany
layoutany
localeany
titleany
descriptionany
isPublicEventboolean
messageany
leadany
customerobjectyes
customer.namestring (min length 2, max length 200)yes
customer.emailstring (email, /^(?:[A-Za-z0-9_'+\-]+\.)*[A-Za-z0-9_'+\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/, max length 200)yes
customer.phoneany
customer.companyany
customer.regCodeany
customer.newsletterConsentboolean
repeatobject
repeat.intervalWeeks1 | 2yes
repeat.endsOnstring (date)yes
previewboolean

Responses

  • 200 Preview of the dates.
    PropertyTypeReq.Description
    datesobject[]yes
    dates[].datestring (date)yes
    dates[].okbooleanyes
    dates[].problemsstring[]yes
  • 201 Series created.
    PropertyTypeReq.Description
    recurrenceIdstring (uuid)yes
    bookingNumberstringyesNumber of the first booking.
    createdobject[]yes
    created[].datestring (date)yes
    created[].numberstringyes
    created[].status"confirmed" | "request"yes
    skippedstring (date)[]yes
  • 400 Validation failed.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 401 Not signed in to the customer portal.
    PropertyTypeReq.Description
    errorstringyes
  • 404 Unknown venue, or the feature is switched off (`feature_off`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 409 The chosen time is not available.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 410 The venue is being closed (`venue_closed`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 429 Per-IP rate limit exceeded.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.

Example

curl -X POST "https://saalo.ee/api/v1/uks-maja/requests/series" \
  -H "Content-Type: application/json" \
  -d '{
    "bookingTypeId": "…",
    "date": "2026-11-20",
    "startTime": "18:00",
    "endTime": "22:00",
    "guests": 1,
    "customer": {
      "name": "Mari Maasikas",
      "email": "mari@example.ee"
    }
  }'

post/api/v1/{venue}/waitlist

Join the waitlist for a taken slot (joinWaitlist)

Body is the booking request plus `waitlistConsent: true`. Only accepted when the chosen rooms and time really clash (otherwise 409 `not_taken`: send a normal request). Books nothing; the oldest waiter gets an email with an offer link when the slot frees up. Rate limit: 10 requests per minute per IP.

Parameters

NameInTypeReq.Description
venuepathstringyesVenue slug, e.g. `uks-maja`.

Request body (application/json)

PropertyTypeReq.Description
bookingTypeIdstring (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)yes
eventTypeIdany
datestring (/^\d{4}-\d{2}-\d{2}$/)yes
endDateany
startTimestring (/^([01]\d|2[0-3]):[0-5]\d$/)yes
endTimestring (/^([01]\d|2[0-3]):[0-5]\d$|^24:00$/)yes
roomIdsstring (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)[]
wholeHouseboolean
guestsinteger (min 1, max 10000)yes
extrasobject[]
extras[].extraIdstring (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)yes
extras[].quantityinteger (min 1, max 1000)
packageIdany
accommodationany
layoutany
localeany
titleany
descriptionany
isPublicEventboolean
messageany
leadany
customerobjectyes
customer.namestring (min length 2, max length 200)yes
customer.emailstring (email, /^(?:[A-Za-z0-9_'+\-]+\.)*[A-Za-z0-9_'+\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/, max length 200)yes
customer.phoneany
customer.companyany
customer.regCodeany
customer.newsletterConsentboolean
waitlistConsenttrueyesRequired, must be `true`: the customer agrees to be emailed when the slot frees up.
website_urlstringHoneypot: leave empty. A filled value is silently dropped.
form_stampstringSigned render time of the venue's form. API clients omit it.

Responses

  • 201 Joined.
    PropertyTypeReq.Description
    oktrueyes
  • 400 Validation failed.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 404 Unknown venue, or the feature is switched off (`feature_off`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 409 The chosen time is not available.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 410 The venue is being closed (`venue_closed`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 429 Per-IP rate limit exceeded.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.

Example

curl -X POST "https://saalo.ee/api/v1/uks-maja/waitlist" \
  -H "Content-Type: application/json" \
  -d '{
    "bookingTypeId": "…",
    "date": "2026-11-20",
    "startTime": "18:00",
    "endTime": "22:00",
    "guests": 1,
    "customer": {
      "name": "Mari Maasikas",
      "email": "mari@example.ee"
    },
    "waitlistConsent": true
  }'

post/api/v1/{venue}/describe

Turn a free-text event description into form values (describeEvent)

The "Describe your event" helper. Never returns prices or availability and stores nothing. 404 `feature_off` when the venue has switched it off or its plan does not include it. Text is limited to 1500 characters. Rate limit: 6 requests per minute per IP.

Parameters

NameInTypeReq.Description
venuepathstringyesVenue slug, e.g. `uks-maja`.
langquery"et" | "en" | "ru" | "lv" | "lt"Return venue-entered names in this language where the venue has translated them.

Request body (application/json)

PropertyTypeReq.Description
textstring (min length 1, max length 1500)yesWhat the customer wrote.
lang"et" | "en" | "ru" | "lv" | "lt"Customer language.
website_urlstringHoneypot: leave empty. A filled value is silently dropped.
form_stampstringSigned render time of the venue's form. API clients omit it.

Responses

  • 200 Suggested form values.
    PropertyTypeReq.Description
    fieldsobjectyes
    fields.bookingTypeIdstring | null (uuid)yes
    fields.eventTypeIdstring | null (uuid)yes
    fields.datestring | null (date)yes
    fields.startTimestring | nullyes
    fields.endTimestring | nullyes
    fields.durationMinutesinteger | nullyes
    fields.guestsinteger | nullyes
    fields.layout"theatre" | "classroom" | "banquet" | "cocktail" | "boardroom" | "u_shape" | "cabaret"yes
    fields.roomIdsstring (uuid)[]yes
    fields.extrasobject[]yes
    fields.extras[].extraIdstring (uuid)yes
    fields.extras[].quantityintegeryes
    filled"bookingType" | "eventType" | "date" | "startTime" | "duration" | "guests" | "layout" | "rooms" | "extras"[]yes
    unclearstring[]yes
    source"ai" | "parser"yes
  • 400 `invalid`, `text_required` or `text_too_long` (with `max`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 404 Unknown venue, or the feature is switched off (`feature_off`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 410 The venue is being closed (`venue_closed`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 429 Per-IP rate limit exceeded.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.

Example

curl -X POST "https://saalo.ee/api/v1/uks-maja/describe" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "…"
  }'

post/api/v1/{venue}/ask

Ask a question about the venue (askQuestion)

The "Ask a question" helper: answers from the venue's published data only; never prices or availability; nothing is stored. 404 `feature_off` when switched off or not in the plan. Rate limit: 6 requests per minute per IP.

Parameters

NameInTypeReq.Description
venuepathstringyesVenue slug, e.g. `uks-maja`.
langquery"et" | "en" | "ru" | "lv" | "lt"Return venue-entered names in this language where the venue has translated them.

Request body (application/json)

PropertyTypeReq.Description
questionstring (min length 1)yesLength limit applies (`question_too_long` with `max`).
lang"et" | "en" | "ru" | "lv" | "lt"Visitor language.
website_urlstringHoneypot: leave empty. A filled value is silently dropped.
form_stampstringSigned render time of the venue's form. API clients omit it.

Responses

  • 200 Answer.
    PropertyTypeReq.Description
    answerstringyes
    sourceIdsstring[]yes
    confidence"answered" | "partial" | "unknown"yes
    handoffbooleanyesTrue when the visitor should ask the team (open the request form).
    source"ai" | "keyword"yes
  • 400 `invalid`, `question_required` or `question_too_long` (with `max`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 404 Unknown venue, or the feature is switched off (`feature_off`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 410 The venue is being closed (`venue_closed`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 429 Per-IP rate limit exceeded.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.

Example

curl -X POST "https://saalo.ee/api/v1/uks-maja/ask" \
  -H "Content-Type: application/json" \
  -d '{
    "question": "…"
  }'

get/api/v1/{venue}/testimonials

Published customer comments (listTestimonials)

Up to 50 newest comments customers agreed to publish: first name plus initial, score, month. No other customer data. Cached for 60 s. Rate limit: 120 requests per minute per IP.

Parameters

NameInTypeReq.Description
venuepathstringyesVenue slug, e.g. `uks-maja`.

Responses

  • 200 Testimonials.
    PropertyTypeReq.Description
    testimonialsobject[]yes
    testimonials[].namestring | nullyesFirst name and initial, e.g. `Anna T.`.
    testimonials[].scoreinteger (min 0, max 10)yes
    testimonials[].monthstring (/^\d{4}-\d{2}$/)yes
    testimonials[].commentstringyes
  • 404 Unknown venue, or the feature is switched off (`feature_off`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 410 The venue is being closed (`venue_closed`).
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.
  • 429 Per-IP rate limit exceeded.
    PropertyTypeReq.Description
    errorstringMachine code, e.g. `not_found`, `rate_limited`, `invalid`, `venue_closed`, `feature_off`, `text_required`.
    problemsProblem | string[]
    maxintegerLength limit, with `text_too_long` / `question_too_long`.

Example

curl "https://saalo.ee/api/v1/uks-maja/testimonials"

get/api/v1/{venue}/ics/{room}

iCalendar feed of a room (getIcsFeed)

Read-only iCalendar feed of a room's blocking bookings and closures (30 days back, 365 days ahead), for subscribing from Google/Apple/Outlook calendars. Events carry no customer data. Access is by the secret `key` shown in Admin; a wrong key, room or venue is a plain 404. Rate limit: 30 requests per minute per IP.

Parameters

NameInTypeReq.Description
venuepathstringyesVenue slug, e.g. `uks-maja`.
roompathstringyesRoom id, or `all` for every room.
keyquerystringyesFeed key from Admin (HMAC; not an API key).

Responses

  • 200 iCalendar document. text/calendar
  • 404 Not found or wrong key (plain text). text/plain
  • 429 Rate limit exceeded (plain text). text/plain

Example

curl "https://saalo.ee/api/v1/uks-maja/ics/all?key=KEY"

REST

Key-authenticated, read-only data API.

get/api/v1/rest/me

Venue and key info (getMe)

The venue and scopes of the API key; handy as a "test authentication" call. Any valid key works.

Auth: Authorization: Bearer vb_live_…

Responses

  • 200 Venue and key.
    PropertyTypeReq.Description
    venueobjectyes
    venue.idstring (uuid)yes
    venue.namestringyes
    venue.slugstringyes
    venue.timezonestringyes
    venue.currencystringyes
    keyobjectyes
    key.idstring (uuid)yes
    key.namestringyes
    key.scopes"bookings:read" | "customers:read"[]yes
  • 401 Missing, invalid or revoked API key.
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes
  • 403 `insufficient_scope` (the key lacks the required scope) or `api_not_available` (the venue's plan has no API).
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes
  • 429 `rate_limited`: more than 60 requests per minute for this key.
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes

Example

curl "https://saalo.ee/api/v1/rest/me" \
  -H "Authorization: Bearer $VENUE_API_KEY"

get/api/v1/rest/bookings

List bookings (listBookings)

Bookings of the key's venue, newest first, with cursor pagination. Requires the `bookings:read` scope.

Auth: Authorization: Bearer vb_live_… scope bookings:read

Parameters

NameInTypeReq.Description
statusquerystringOne or more comma-separated statuses: new_request, quote_drafting, awaiting_staff, quote_sent, awaiting_customer, confirmed, cancelled, completed.
updated_sincequerystringISO 8601 date or datetime; only bookings with `updated_at >= value`.
fromquerystringISO 8601; only bookings with `starts_at >= from` (a bare date is UTC midnight).
toquerystringISO 8601; only bookings with `starts_at < to`.
sortquery"created_at" | "updated_at"Sort key, newest first. Use `updated_at` to follow changes.
limitqueryinteger (min 1, max 100)Page size, 1 to 100 (default 25).
cursorquerystring`next_cursor` of the previous page. Opaque; use it with the same `sort`.

Responses

  • 200 A page of bookings.
    PropertyTypeReq.Description
    dataBooking[]yes
    data[].idstring (uuid)yes
    data[].numberstringyesBooking number.
    data[].status"new_request" | "quote_drafting" | "awaiting_staff" | "quote_sent" | "awaiting_customer" | "confirmed" | "cancelled" | "completed"yes
    data[].starts_atstring (date-time)yes
    data[].ends_atstring (date-time)yes
    data[].guestsintegeryes
    data[].whole_housebooleanyes
    data[].roomsobject[]yes
    data[].rooms[].idstring (uuid)yes
    data[].rooms[].namestringyes
    data[].customer_idstring | null (uuid)yes
    data[].total_gross_centsinteger | nullyesGross total of the accepted quote in cents; null until a quote is accepted.
    data[].currencystringyesISO 4217, e.g. `EUR`.
    data[].created_atstring (date-time)yes
    data[].updated_atstring (date-time)yes
  • 400 `invalid_parameter`: the message names the parameter.
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes
  • 401 Missing, invalid or revoked API key.
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes
  • 403 `insufficient_scope` (the key lacks the required scope) or `api_not_available` (the venue's plan has no API).
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes
  • 429 `rate_limited`: more than 60 requests per minute for this key.
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes

Example

curl "https://saalo.ee/api/v1/rest/bookings" \
  -H "Authorization: Bearer $VENUE_API_KEY"

get/api/v1/rest/bookings/{id}

Get one booking (getBooking)

One booking of the key's venue. Requires the `bookings:read` scope.

Auth: Authorization: Bearer vb_live_… scope bookings:read

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesBooking id (UUID).

Responses

  • 200 The booking.
    PropertyTypeReq.Description
    dataBookingyes
    data.idstring (uuid)yes
    data.numberstringyesBooking number.
    data.status"new_request" | "quote_drafting" | "awaiting_staff" | "quote_sent" | "awaiting_customer" | "confirmed" | "cancelled" | "completed"yes
    data.starts_atstring (date-time)yes
    data.ends_atstring (date-time)yes
    data.guestsintegeryes
    data.whole_housebooleanyes
    data.roomsobject[]yes
    data.rooms[].idstring (uuid)yes
    data.rooms[].namestringyes
    data.customer_idstring | null (uuid)yes
    data.total_gross_centsinteger | nullyesGross total of the accepted quote in cents; null until a quote is accepted.
    data.currencystringyesISO 4217, e.g. `EUR`.
    data.created_atstring (date-time)yes
    data.updated_atstring (date-time)yes
  • 401 Missing, invalid or revoked API key.
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes
  • 403 `insufficient_scope` (the key lacks the required scope) or `api_not_available` (the venue's plan has no API).
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes
  • 404 `not_found`: no such record in this venue.
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes
  • 429 `rate_limited`: more than 60 requests per minute for this key.
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes

Example

curl "https://saalo.ee/api/v1/rest/bookings/{id}" \
  -H "Authorization: Bearer $VENUE_API_KEY"

get/api/v1/rest/customers

List customers (listCustomers)

Customers of the key's venue, newest first (by creation; customers have no `updated_at`, so there is no `updated_since`). GDPR-erased customers have `erased: true` and null contact fields. Requires the `customers:read` scope.

Auth: Authorization: Bearer vb_live_… scope customers:read

Parameters

NameInTypeReq.Description
limitqueryinteger (min 1, max 100)Page size, 1 to 100 (default 25).
cursorquerystring`next_cursor` of the previous page. Opaque; use it with the same `sort`.

Responses

  • 200 A page of customers.
    PropertyTypeReq.Description
    dataCustomer[]yes
    data[].idstring (uuid)yes
    data[].namestring | nullyes
    data[].emailstring | nullyes
    data[].phonestring | nullyes
    data[].companystring | nullyes
    data[].created_atstring (date-time)yes
    data[].erasedbooleanyesGDPR-erased: contact fields are null.
  • 400 `invalid_parameter`: the message names the parameter.
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes
  • 401 Missing, invalid or revoked API key.
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes
  • 403 `insufficient_scope` (the key lacks the required scope) or `api_not_available` (the venue's plan has no API).
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes
  • 429 `rate_limited`: more than 60 requests per minute for this key.
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes

Example

curl "https://saalo.ee/api/v1/rest/customers" \
  -H "Authorization: Bearer $VENUE_API_KEY"

get/api/v1/rest/customers/{id}

Get one customer (getCustomer)

One customer of the key's venue. Requires the `customers:read` scope.

Auth: Authorization: Bearer vb_live_… scope customers:read

Parameters

NameInTypeReq.Description
idpathstring (uuid)yesCustomer id (UUID).

Responses

  • 200 The customer.
    PropertyTypeReq.Description
    dataCustomeryes
    data.idstring (uuid)yes
    data.namestring | nullyes
    data.emailstring | nullyes
    data.phonestring | nullyes
    data.companystring | nullyes
    data.created_atstring (date-time)yes
    data.erasedbooleanyesGDPR-erased: contact fields are null.
  • 401 Missing, invalid or revoked API key.
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes
  • 403 `insufficient_scope` (the key lacks the required scope) or `api_not_available` (the venue's plan has no API).
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes
  • 404 `not_found`: no such record in this venue.
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes
  • 429 `rate_limited`: more than 60 requests per minute for this key.
    PropertyTypeReq.Description
    errorstringyes`unauthorized`, `insufficient_scope`, `api_not_available`, `invalid_parameter`, `not_found` or `rate_limited`.
    messagestringyes

Example

curl "https://saalo.ee/api/v1/rest/customers/{id}" \
  -H "Authorization: Bearer $VENUE_API_KEY"

Webhooks

Events the platform sends to your URL.

webhook booking.confirmed

A booking was confirmed (the customer accepted the quote, or staff confirmed it). Sent as an HTTPS POST to each URL configured under Admin > Integrations that subscribes to this event (Pro plan). The body carries ids, number, times and status only, no customer contact data; fetch the customer through the REST API (`customer_id`). Verify `x-venue-signature` (HMAC-SHA256 of the raw body, hex, prefixed `sha256=`) before trusting the payload. Reply 2xx within 5 seconds; redirects are not followed. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h and 12 h with the same `x-venue-delivery` id and body, then marked dead.

Headers

  • x-venue-signature: `sha256=` + hex HMAC-SHA256 of the raw request body, keyed with the webhook's signing secret.
  • x-venue-event: The event name.
  • x-venue-delivery: Unique id of this delivery; retries reuse it, so use it to de-duplicate.

Body

PropertyTypeReq.Description
event"booking.confirmed"yesEvent name.
occurred_atstring (date-time)yesWhen the event was emitted (ISO 8601, UTC).
dataWebhookBookingDatayes
data.bookingIdstring (uuid)yes
data.numberstringyesBooking number.
data.status"new_request" | "quote_drafting" | "awaiting_staff" | "quote_sent" | "awaiting_customer" | "confirmed" | "cancelled" | "completed"yes
data.startsAtstring (date-time)yes
data.endsAtstring (date-time)yes
data.guestsintegeryes
{
  "event": "booking.confirmed",
  "occurred_at": "2026-11-05T12:00:03.120Z",
  "data": {
    "bookingId": "6f1c…",
    "number": "B-2026-00042",
    "status": "confirmed",
    "startsAt": "2026-11-20T16:00:00.000Z",
    "endsAt": "2026-11-20T20:00:00.000Z",
    "guests": 40
  }
}

webhook booking.cancelled

A booking was cancelled. Sent as an HTTPS POST to each URL configured under Admin > Integrations that subscribes to this event (Pro plan). The body carries ids, number, times and status only, no customer contact data; fetch the customer through the REST API (`customer_id`). Verify `x-venue-signature` (HMAC-SHA256 of the raw body, hex, prefixed `sha256=`) before trusting the payload. Reply 2xx within 5 seconds; redirects are not followed. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h and 12 h with the same `x-venue-delivery` id and body, then marked dead.

Headers

  • x-venue-signature: `sha256=` + hex HMAC-SHA256 of the raw request body, keyed with the webhook's signing secret.
  • x-venue-event: The event name.
  • x-venue-delivery: Unique id of this delivery; retries reuse it, so use it to de-duplicate.

Body

PropertyTypeReq.Description
event"booking.cancelled"yesEvent name.
occurred_atstring (date-time)yesWhen the event was emitted (ISO 8601, UTC).
dataWebhookBookingDatayes
data.bookingIdstring (uuid)yes
data.numberstringyesBooking number.
data.status"new_request" | "quote_drafting" | "awaiting_staff" | "quote_sent" | "awaiting_customer" | "confirmed" | "cancelled" | "completed"yes
data.startsAtstring (date-time)yes
data.endsAtstring (date-time)yes
data.guestsintegeryes
{
  "event": "booking.cancelled",
  "occurred_at": "2026-11-05T12:00:03.120Z",
  "data": {
    "bookingId": "6f1c…",
    "number": "B-2026-00042",
    "status": "confirmed",
    "startsAt": "2026-11-20T16:00:00.000Z",
    "endsAt": "2026-11-20T20:00:00.000Z",
    "guests": 40
  }
}

webhook booking.changed

A booking's time, rooms or details were changed. Sent as an HTTPS POST to each URL configured under Admin > Integrations that subscribes to this event (Pro plan). The body carries ids, number, times and status only, no customer contact data; fetch the customer through the REST API (`customer_id`). Verify `x-venue-signature` (HMAC-SHA256 of the raw body, hex, prefixed `sha256=`) before trusting the payload. Reply 2xx within 5 seconds; redirects are not followed. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h and 12 h with the same `x-venue-delivery` id and body, then marked dead.

Headers

  • x-venue-signature: `sha256=` + hex HMAC-SHA256 of the raw request body, keyed with the webhook's signing secret.
  • x-venue-event: The event name.
  • x-venue-delivery: Unique id of this delivery; retries reuse it, so use it to de-duplicate.

Body

PropertyTypeReq.Description
event"booking.changed"yesEvent name.
occurred_atstring (date-time)yesWhen the event was emitted (ISO 8601, UTC).
dataWebhookBookingDatayes
data.bookingIdstring (uuid)yes
data.numberstringyesBooking number.
data.status"new_request" | "quote_drafting" | "awaiting_staff" | "quote_sent" | "awaiting_customer" | "confirmed" | "cancelled" | "completed"yes
data.startsAtstring (date-time)yes
data.endsAtstring (date-time)yes
data.guestsintegeryes
{
  "event": "booking.changed",
  "occurred_at": "2026-11-05T12:00:03.120Z",
  "data": {
    "bookingId": "6f1c…",
    "number": "B-2026-00042",
    "status": "confirmed",
    "startsAt": "2026-11-20T16:00:00.000Z",
    "endsAt": "2026-11-20T20:00:00.000Z",
    "guests": 40
  }
}

webhook request.created

A new booking request arrived through the public API, the widget or the waitlist. Sent as an HTTPS POST to each URL configured under Admin > Integrations that subscribes to this event (Pro plan). The body carries ids, number, times and status only, no customer contact data; fetch the customer through the REST API (`customer_id`). Verify `x-venue-signature` (HMAC-SHA256 of the raw body, hex, prefixed `sha256=`) before trusting the payload. Reply 2xx within 5 seconds; redirects are not followed. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h and 12 h with the same `x-venue-delivery` id and body, then marked dead.

Headers

  • x-venue-signature: `sha256=` + hex HMAC-SHA256 of the raw request body, keyed with the webhook's signing secret.
  • x-venue-event: The event name.
  • x-venue-delivery: Unique id of this delivery; retries reuse it, so use it to de-duplicate.

Body

PropertyTypeReq.Description
event"request.created"yesEvent name.
occurred_atstring (date-time)yesWhen the event was emitted (ISO 8601, UTC).
dataWebhookBookingDatayes
data.bookingIdstring (uuid)yes
data.numberstringyesBooking number.
data.status"new_request" | "quote_drafting" | "awaiting_staff" | "quote_sent" | "awaiting_customer" | "confirmed" | "cancelled" | "completed"yes
data.startsAtstring (date-time)yes
data.endsAtstring (date-time)yes
data.guestsintegeryes
{
  "event": "request.created",
  "occurred_at": "2026-11-05T12:00:03.120Z",
  "data": {
    "bookingId": "6f1c…",
    "number": "B-2026-00042",
    "status": "confirmed",
    "startsAt": "2026-11-20T16:00:00.000Z",
    "endsAt": "2026-11-20T20:00:00.000Z",
    "guests": 40
  }
}