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>
- query:
Origin checks
- Browser integrations should be used from a domain that is allowed for the tenant.
- Send normal browser
OriginorRefererheaders. 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>
- header
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>
- query/body:
tenant=<slug>orX-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:
- Get the chatbot widget key from the tenant's Resevu chatbot settings.
- Confirm the website domain is allowed for the tenant.
- When the chatbot is embedded on a page without the booking widget, set
Boekingspagina voor chatbotlinksinAdmin > Chatbot > Instellingento the page that contains the booking widget or custom booking flow. - 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_keyorX-Chatbot-KeyrecommendedtenantorX-Tenantfor controlled integrations
- Response:
successtenant { 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_keyorX-Chatbot-Key
- JSON body:
localeoptional, one of configured app localeschanneloptional, normallywidget
- Response:
successconversation { id, locale, is_test }
POST /chatbot/conversations/{conversation}/messages
- Query/header:
widget_keyorX-Chatbot-Key
- Path:
conversationpublic conversation UUID
- JSON body:
messagerequired, max 4000 charspage_urloptional
- Response:
successconversation { id, status, locale, is_test }message { role, content }fallback,fallback_reasonusage { provider, model, credits_delta }
POST /chatbot/conversations/{conversation}/handoff
- Query/header:
widget_keyorX-Chatbot-Key
- Path:
conversationpublic conversation UUID
- Response:
successconversation { id, status }, with statusneeds_review
GET /public/bookings/bootstrap
- Query:
tenant(orX-Tenantheader)
- 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_durationinitial_price
GET /public/bookings/employees
- Query:
tenant(orX-Tenantheader)days_ahead(optional integer, capped by tenantmax_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:
successdata[]with:id,name,photo_url,biomassage_types[](active massage types the employee can perform)available_dates[](Y-m-dvalues for the requested window)
meta { days_ahead, start_date, massage_type_id }
GET /public/bookings/massage-types
- Query:
tenant(orX-Tenantheader)
- Response:
successmassage_types[]
GET /public/bookings/available-durations
- Query:
tenant(orX-Tenant)massage_type_id(required)is_duo(optional boolean)second_massage_type_id(optional)date(optionalY-m-d)
- Response includes:
durations[]deposit_type,deposit_value,deposit_is_full,can_pay_full
GET /public/bookings/available-time-slots
- Query:
tenant(orX-Tenant)massage_type_id(required)duration_id(required)date(requiredY-m-d)is_duo(optional boolean)second_massage_type_id(optional)
- Response includes:
time_slots[]employee_assignmentsmapis_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(orX-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(orX-Tenant)
- Response includes:
currency_code,currency_symbol
POST /public/bookings/store
- Query:
tenant(orX-Tenant)
- JSON body:
name(required)firstname(required)massage_type_id(required)duration(required)date(required)start_time(requiredH:i)email,phone,coupon_code,is_duo,second_massage_type_id,redirect_domain,tracking_url,payment_type,language(optional)
- Response:
successredirect_urlwhen accepted
GET /public/bookings/available-days
- Query:
tenant(orX-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:
successdates[](availableY-m-dvalues)
- 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, oremployee_id, this endpoint returns only dates where the requested combination can actually be scheduled. - Pass
duration_idwhen the frontend needs exact availability for the selected service duration.
GET /public/bookings/coupon/validate
- Query:
tenant(orX-Tenant)code(required)massage_type_id(optional)
- Response:
- Coupon fields (
id,code,type,value) or 404 when invalid.
- Coupon fields (
POST /public/vouchers/initiate
- Query:
tenant(orX-Tenant)
- JSON body:
buyer_name(required)buyer_email(required)amount(required, numeric, min 0)redirect_domain,tracking_url,meta,language(optional)
- Response:
successredirect_urlorder_id
GET /payment/start/{booking}
- Path:
booking(booking id)
- Query:
tenant(orX-Tenant)type(optional:depositorfull)redirect_domain(optional)
- Response:
- Redirects (
303) to the payment checkout URL.
- Redirects (
GET /payment/start-voucher/{order}
- Path:
order(gift voucher order id)
- Query:
tenant(orX-Tenant)
- Response:
- Redirects (
303) to the payment checkout URL.
- Redirects (
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(orX-Tenant)api_key(optional fallback to header)
- Headers:
OriginrecommendedX-Api-Keyoptional/required per tenant policy
- Response:
data.tenant,data.currency,data.timezone,data.booking,data.deposit,data.features
GET /public/opening-hours
- Query:
tenant(orX-Tenant)api_keyoptional
- Response:
data.timezonedata.opening_hours[]
GET /public/employees
- Query:
tenant(orX-Tenant)api_keyoptional
- Response:
- employee list with
massage_types.
- employee list with
GET /public/employees/available
- Query:
tenant(orX-Tenant)api_keyoptionaldate(required)duration_minutes(required)is_duo,massage_type_id,second_massage_type_id,time(optional)
- Response:
time_slots[]employee_assignmentsavailable_employee_ids[]is_duo
GET /public/employees/{employee}/availability
- Path:
employee(employee id in current tenant)
- Query:
tenant(orX-Tenant)api_keyoptionaldate(required)duration_minutes(required)massage_type_id(optional)
- Response:
employee_idtime_slots[]employee_assignments
GET /public/pricing/estimate
- Query:
tenant(orX-Tenant)api_keyoptionalduration_id(required)coupon_code,massage_type_id,date,is_duo,second_massage_type_id(optional)- If
is_duois omitted andsecond_massage_type_idis 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 }