Public API Reference

Use the sidebar to jump by endpoint or search inside the rendered documentation.

Base URL https://www.resevu.com/api 27 endpoints 39 sections
No matching sections found.

API Documentation

Base URL: https://resevu.com/api

This document describes the public endpoints available for website widgets and custom integrations.

Quick links:

  • HTML overview: GET https://resevu.com/api/
  • JSON info: GET https://resevu.com/api/info
  • OpenAPI YAML: GET https://resevu.com/resources/docs/public-api.yaml

Access, tenancy, and security

Tenant resolution

  • Most endpoints require a tenant context via:
    • query: tenant=<slug>, or
    • header: X-Tenant: <slug>

Origin checks

  • Browser integrations should be used from a domain that is allowed for the tenant.
  • Send normal browser Origin or Referer headers. The API can reject requests from unapproved domains.

Public API keys

  • Some /public/* endpoints require a public API key depending on the tenant configuration.
  • API key can be sent as:
    • header X-Api-Key: <key>, or
    • query api_key=<key>

Chatbot widget access

  • The chatbot script is served from GET https://resevu.com/chatbot/widget.js.
  • Preferred tenant resolution is the public chatbot widget key:
    • query/body: widget_key=<widget_key>, or
    • header: X-Chatbot-Key: <widget_key>
  • tenant=<slug> or X-Tenant: <slug> also works for controlled integrations, but external websites should use the widget key.

Signed URLs

  • Calendar download links must use the signed URL generated by Resevu.

Rate limits

  • Public endpoints are rate limited. If you receive 429 Too Many Requests, retry later and reduce request frequency.

Endpoint catalog

Method Path Group Notes
GET / Info API overview page
GET /info Info Machine-readable endpoint list
GET /chatbot/config Chatbot Widget config, tenant info, styling, messages
POST /chatbot/conversations Chatbot Start a public chatbot conversation
POST /chatbot/conversations/{conversation}/messages Chatbot Send message and receive assistant reply
POST /chatbot/conversations/{conversation}/handoff Chatbot Mark conversation as needing tenant follow-up
GET /public/bookings/bootstrap Widget booking Tenant required
GET /public/bookings/employees Widget booking Tenant required
GET /public/bookings/massage-types Widget booking Tenant required
GET /public/bookings/available-durations Widget booking Tenant required
GET /public/bookings/available-time-slots Widget booking Tenant required
GET /public/bookings/price-estimate Widget booking Tenant required
GET /public/bookings/widget-config Widget booking Tenant required
POST /public/bookings/store Widget booking Tenant required
GET /public/bookings/available-days Widget booking Tenant required
GET /public/bookings/coupon/validate Widget booking Tenant required
POST /public/vouchers/initiate Voucher Tenant required
GET /payment/start/{booking} Payment Tenant required, redirects to payment checkout
GET /payment/start-voucher/{order} Payment Tenant required, redirects to payment checkout
POST /payment/webhook Payment Payment provider callback
GET /public/tenants/{tenant}/bookings/{booking}/calendar.ics Calendar Signed URL required
GET /public/config Public API v1 Tenant + API key when required
GET /public/opening-hours Public API v1 Tenant + API key when required
GET /public/employees Public API v1 Tenant + API key when required
GET /public/employees/available Public API v1 Tenant + API key when required
GET /public/employees/{employee}/availability Public API v1 Tenant + API key when required
GET /public/pricing/estimate Public API v1 Tenant + API key when required

Availability behavior

Availability endpoints return bookable dates and start times for the selected tenant, service, duration, employee, and duo settings.

Availability can change between reading slots and creating a booking. Always treat POST /public/bookings/store as the final confirmation step. A slot returned earlier can still be rejected if it was booked or blocked before the create request.

Chatbot widget setup

The chatbot is an optional tenant feature. To embed it on a website:

  1. Get the chatbot widget key from the tenant's Resevu chatbot settings.
  2. Confirm the website domain is allowed for the tenant.
  3. When the chatbot is embedded on a page without the booking widget, set Boekingspagina voor chatbotlinks in Admin > Chatbot > Instellingen to the page that contains the booking widget or custom booking flow.
  4. Place the generated embed code on the tenant website:
<script src="https://resevu.com/chatbot/widget.js" data-resevu-chatbot-key="<widget_key>" defer></script>

Optional attributes:

<script
  src="https://resevu.com/chatbot/widget.js"
  data-resevu-chatbot-key="<widget_key>"
  data-locale="nl"
  data-api-base="https://resevu.com/api"
  defer
></script>

Chatbot booking handoff

When prepare_booking_checkout is enabled, the chatbot may prepare a booking link. It first gathers treatment, duration, date, and start time. If details are missing, it asks one compact follow-up question for those missing details only.

When all details are ready, the chatbot asks the customer to type bevestigen. The booking link is appended only after that final confirmation. The link is based on the configured Boekingspagina voor chatbotlinks when present; otherwise the submitted page_url is used. Generated links include resevu_booking=1, massage_type_id, duration_minutes, date, and start_time.

Endpoint details

GET /

  • Returns HTML API overview page.

GET /info

  • Returns JSON with metadata, links, and endpoint list.

GET /chatbot/config

  • Query/header:
    • widget_key or X-Chatbot-Key recommended
    • tenant or X-Tenant for controlled integrations
  • Response:
    • success
    • tenant { slug, name }
    • chatbot { bot_name, welcome_messages, fallback_messages, allowed_actions, primary_color, accent_color, avatar_path, privacy_notice }
    • credits { balance }

POST /chatbot/conversations

  • Query/header:
    • widget_key or X-Chatbot-Key
  • JSON body:
    • locale optional, one of configured app locales
    • channel optional, normally widget
  • Response:
    • success
    • conversation { id, locale, is_test }

POST /chatbot/conversations/{conversation}/messages

  • Query/header:
    • widget_key or X-Chatbot-Key
  • Path:
    • conversation public conversation UUID
  • JSON body:
    • message required, max 4000 chars
    • page_url optional
  • Response:
    • success
    • conversation { id, status, locale, is_test }
    • message { role, content }
    • fallback, fallback_reason
    • usage { provider, model, credits_delta }

POST /chatbot/conversations/{conversation}/handoff

  • Query/header:
    • widget_key or X-Chatbot-Key
  • Path:
    • conversation public conversation UUID
  • Response:
    • success
    • conversation { id, status }, with status needs_review

GET /public/bookings/bootstrap

  • Query:
    • tenant (or X-Tenant header)
  • Recommended for first widget load to reduce startup round-trips.
  • Response includes:
    • config (tenant, currency, timezone, booking, deposit)
    • employees[] (active employees + linked active massage types)
    • massage_types[] with:
      • durations[] (effective for today)
      • initial_duration
      • initial_price

GET /public/bookings/employees

  • Query:
    • tenant (or X-Tenant header)
    • days_ahead (optional integer, capped by tenant max_days_in_advance)
    • massage_type_id (optional, active massage type in current tenant)
  • Recommended when the frontend wants a larger employee dataset up front to reduce follow-up requests.
  • Response includes:
    • success
    • data[] with:
      • id, name, photo_url, bio
      • massage_types[] (active massage types the employee can perform)
      • available_dates[] (Y-m-d values for the requested window)
    • meta { days_ahead, start_date, massage_type_id }

GET /public/bookings/massage-types

  • Query:
    • tenant (or X-Tenant header)
  • Response:
    • success
    • massage_types[]

GET /public/bookings/available-durations

  • Query:
    • tenant (or X-Tenant)
    • massage_type_id (required)
    • is_duo (optional boolean)
    • second_massage_type_id (optional)
    • date (optional Y-m-d)
  • Response includes:
    • durations[]
    • deposit_type, deposit_value, deposit_is_full, can_pay_full

GET /public/bookings/available-time-slots

  • Query:
    • tenant (or X-Tenant)
    • massage_type_id (required)
    • duration_id (required)
    • date (required Y-m-d)
    • is_duo (optional boolean)
    • second_massage_type_id (optional)
  • Response includes:
    • time_slots[]
    • employee_assignments map
    • is_duo
  • Notes:
    • A returned start time means at least one suitable employee, or employee pair for duo, has enough continuous free 15-minute blocks for the selected duration.
    • Existing bookings block their assigned employees, including configured buffer time between massages.

GET /public/bookings/price-estimate

  • Query:
    • tenant (or X-Tenant)
    • duration_id (required)
    • massage_type_id (required)
    • coupon_code (optional)
    • date (optional)
    • is_duo (optional boolean)
    • second_massage_type_id (optional)
  • Response includes:
    • price { base, discount, total }
    • deposit { type, value, is_full, amount, can_pay_full }

GET /public/bookings/widget-config

  • Query:
    • tenant (or X-Tenant)
  • Response includes:
    • currency_code, currency_symbol

POST /public/bookings/store

  • Query:
    • tenant (or X-Tenant)
  • JSON body:
    • name (required)
    • firstname (required)
    • massage_type_id (required)
    • duration (required)
    • date (required)
    • start_time (required H:i)
    • email, phone, coupon_code, is_duo, second_massage_type_id, redirect_domain, tracking_url, payment_type, language (optional)
  • Response:
    • success
    • redirect_url when accepted

GET /public/bookings/available-days

  • Query:
    • tenant (or X-Tenant)
    • is_duo (optional boolean)
    • massage_type_id (optional)
    • duration_id (optional)
    • second_massage_type_id (optional)
    • employee_id (optional, active employee in current tenant)
    • days_ahead (optional)
  • Response:
    • success
    • dates[] (available Y-m-d values)
  • Notes:
    • Without service, duration, duo, or employee filters, this endpoint returns open shop days.
    • With massage_type_id, duration_id, is_duo, second_massage_type_id, or employee_id, this endpoint returns only dates where the requested combination can actually be scheduled.
    • Pass duration_id when the frontend needs exact availability for the selected service duration.

GET /public/bookings/coupon/validate

  • Query:
    • tenant (or X-Tenant)
    • code (required)
    • massage_type_id (optional)
  • Response:
    • Coupon fields (id, code, type, value) or 404 when invalid.

POST /public/vouchers/initiate

  • Query:
    • tenant (or X-Tenant)
  • JSON body:
    • buyer_name (required)
    • buyer_email (required)
    • amount (required, numeric, min 0)
    • redirect_domain, tracking_url, meta, language (optional)
  • Response:
    • success
    • redirect_url
    • order_id

GET /payment/start/{booking}

  • Path:
    • booking (booking id)
  • Query:
    • tenant (or X-Tenant)
    • type (optional: deposit or full)
    • redirect_domain (optional)
  • Response:
    • Redirects (303) to the payment checkout URL.

GET /payment/start-voucher/{order}

  • Path:
    • order (gift voucher order id)
  • Query:
    • tenant (or X-Tenant)
  • Response:
    • Redirects (303) to the payment checkout URL.

POST /payment/webhook

  • Body:
    • id (payment provider transaction id)
  • Response:
    • status update JSON.

GET /public/tenants/{tenant}/bookings/{booking}/calendar.ics

  • Path:
    • tenant (tenant slug)
    • booking (booking id)
  • Query:
    • Signed URL parameters generated by backend.
  • Response:
    • ICS file download.

GET /public/config

  • Query:
    • tenant (or X-Tenant)
    • api_key (optional fallback to header)
  • Headers:
    • Origin recommended
    • X-Api-Key optional/required per tenant policy
  • Response:
    • data.tenant, data.currency, data.timezone, data.booking, data.deposit, data.features

GET /public/opening-hours

  • Query:
    • tenant (or X-Tenant)
    • api_key optional
  • Response:
    • data.timezone
    • data.opening_hours[]

GET /public/employees

  • Query:
    • tenant (or X-Tenant)
    • api_key optional
  • Response:
    • employee list with massage_types.

GET /public/employees/available

  • Query:
    • tenant (or X-Tenant)
    • api_key optional
    • date (required)
    • duration_minutes (required)
    • is_duo, massage_type_id, second_massage_type_id, time (optional)
  • Response:
    • time_slots[]
    • employee_assignments
    • available_employee_ids[]
    • is_duo

GET /public/employees/{employee}/availability

  • Path:
    • employee (employee id in current tenant)
  • Query:
    • tenant (or X-Tenant)
    • api_key optional
    • date (required)
    • duration_minutes (required)
    • massage_type_id (optional)
  • Response:
    • employee_id
    • time_slots[]
    • employee_assignments

GET /public/pricing/estimate

  • Query:
    • tenant (or X-Tenant)
    • api_key optional
    • duration_id (required)
    • coupon_code, massage_type_id, date, is_duo, second_massage_type_id (optional)
    • If is_duo is omitted and second_massage_type_id is provided, the API infers duo mode automatically.
  • Response:
    • currency { code, symbol }
    • price { base, discount, total, details }
    • deposit { type, value, is_full, amount, can_pay_full }