REST API Reference
Reservly exposes 7 public endpoints under /api/public/{slug}/. All GET endpoints are publicly accessible without authentication. The POST /book endpoint requires an API key — pass it as Authorization: Bearer rsvly_....
Localization
Business-authored content (names, descriptions, staff bios, ticket-tier names) can be translated. By default every endpoint returns the raw translation maps — name_translations, description_translations, category_translations, bio_translations — and leaves it to you to pick a language.
Alternatively, add ?locale= to /services, /staff, /events or /rentals and Reservly resolves them for you. The base fields carry the translated text, and the now-redundant *_translations keys are omitted from the response. Nested collections are resolved too — events[].inventory[] and add_on_rentals[].
The parameter is fully optional and never fails a request. Omit it and the response is byte-for-byte what it has always been, so existing integrations are unaffected. Supply a language the business does not offer — or a malformed value — and you get the business's own default content with a 200, not an error. The languages a business supports are listed as languages on /info.
Two limits worth knowing: /info does not accept locale (it returns no translatable fields), and rental category has no translations in the data model, so it is returned untranslated.
Deprecations
short_description and short_description_translations were removed on 2026-08-20 (FC-42 — announced for 2026-09-19, removed early pre-launch by founder decision; no registered consumers existed). Read description / description_translations.
Pricing quotes
Catalogue prices are list prices. A business's pricing rules (seasonal, day-of-week, flash-sale, early-bird…) can move what a booking actually costs, so /services, /events and /rentals also return quoted_price beside every price: the price of one unit computed by the same pricing engine the booking flow charges with. Each response carries a quote object stating what the quote assumes. Quote from quoted_price, never price — the list price is not what the customer will pay when rules apply.
Services and rentals quote for the business's current date by default; pass ?date=YYYY-MM-DD to quote a specific booking date (a malformed date returns 400). Events always quote for the event's own date and start time. For rentals the quoted number is the rules-adjusted rate (see price_type). Rules that need a booking time, a cart, or a customer identity are decided at booking time — POST /book returns the authoritative total, and quoted_price is null for unpriced (free) items or when quote.applies_pricing_rules is false.
Business Info
Example Request
curl https://reservly.io/api/public/luxe-salon/infobash
Example Response
{
"name": "Luxe Salon",
"slug": "luxe-salon",
"logo_url": "https://cdn.reservly.io/logos/luxe-salon.png",
"currency": "USD",
"timezone": "America/New_York",
"default_language": "en",
"languages": ["en", "es"],
"modules_enabled": {
"services": true,
"events": false,
"rentals": false
},
"social_links": {
"instagram": "https://instagram.com/luxesalon"
},
"action_buttons": [
{
"type": "link",
"label": "Visit Website",
"icon": "globe",
"content": "https://luxesalon.com",
"order": 0
}
],
"has_payment_providers": true,
"payment_providers": ["stripe"],
"cancellation_policy": [
{ "hours_before": 24, "refund_percent": 100 },
{ "hours_before": 2, "refund_percent": 50 }
]
}jsonList Services
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| locale | string | Optional | Resolve name and description into this language and omit the *_translations maps. See Localization. |
| date | string | Optional | Booking date (YYYY-MM-DD) quoted_price should assume. Defaults to the business's current date. Malformed values return 400. See Pricing quotes. |
Example Request
curl https://reservly.io/api/public/luxe-salon/servicesbash
Example Response
{
"services": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Haircut & Style",
"description": "Full haircut with wash and blow-dry, finished with a style. Tell us how you like it and we will match the last cut you loved.",
"duration_minutes": 60,
"price": 75,
"quoted_price": 86.25,
"currency": "USD",
"image_url": "https://cdn.reservly.io/services/haircut.jpg",
"allow_multiple": false,
"has_class_schedule": false,
"name_translations": { "es": "Corte y Peinado" },
"description_translations": null,
"payment_requirement": "full",
"deposit_value": null
}
],
"currency": "USD",
"quote": {
"date": "2026-08-07",
"applies_pricing_rules": true,
"basis": "quoted_price is the engine price of one unit booked on `date` (this business's pricing rules applied). Time-of-day, cart, and customer-specific rules are decided at booking; POST /book returns the authoritative total."
}
}jsonList Staff
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| service_id | uuid | Optional | Filter staff to only those assigned to this service |
| locale | string | Optional | Resolve name, category and description into this language and omit the *_translations maps. Note bio_translations resolves into description. See Localization. |
Example Request
curl "https://reservly.io/api/public/luxe-salon/staff?service_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890"bash
Example Response
{
"staff": [
{
"id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"name": "Maria Garcia",
"description": "Senior stylist with 10 years experience",
"category": "Stylists",
"image_url": "https://cdn.reservly.io/staff/maria.jpg",
"bio_translations": { "es": "Estilista senior con 10 anos de experiencia" }
}
]
}jsonAvailable Slots
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| date | string | Required | Date in YYYY-MM-DD format |
| service_id | uuid | Required | Service UUID. Supports comma-separated IDs for multi-service bookings. |
| staff_id | uuid | Optional | Filter slots to a specific staff member |
| is_package | string | Optional | Set to "true" when booking a package (uses any-staff matching) |
Example Request
curl "https://reservly.io/api/public/luxe-salon/slots?date=2026-04-15&service_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890"bash
Example Response
{
"slots": [
{
"time": "09:00",
"endTime": "10:00",
"staffId": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"staffName": "Maria Garcia"
},
{
"time": "10:00",
"endTime": "11:00",
"staffId": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"staffName": "Maria Garcia"
},
{
"time": "10:00",
"endTime": "11:00",
"staffId": "b2c3d4e5-a6b7-8901-cdef-234567890abc",
"staffName": "Alex Kim"
}
],
"timezone": "America/New_York"
}jsonRate limit: 60 requests / minute per IP
Upcoming Events
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| locale | string | Optional | Resolve event name and description — and the nested inventory[] ticket-tier names — into this language, omitting the *_translations maps. See Localization. |
Example Request
curl https://reservly.io/api/public/luxe-salon/eventsbash
Example Response
{
"events": [
{
"id": "c3d4e5f6-a7b8-9012-cdef-345678901234",
"name": "Summer Hair Workshop",
"description": "Learn the latest styling techniques in a three-hour hands-on workshop. Tools and a take-home kit are provided; no experience needed.",
"date": "2026-05-20",
"start_time": "14:00",
"end_time": "17:00",
"image_url": "https://cdn.reservly.io/events/workshop.jpg",
"total_capacity": 30,
"spots_remaining": 18,
"inventory": [
{
"id": "d4e5f6a7-b8c9-0123-def4-567890123456",
"name": "General Admission",
"price": 45,
"quoted_price": 50,
"total_capacity": 20,
"spots_remaining": 12,
"name_translations": null
},
{
"id": "e5f6a7b8-c9d0-1234-ef56-789012345678",
"name": "VIP Front Row",
"price": 85,
"quoted_price": 93.5,
"total_capacity": 10,
"spots_remaining": 6,
"name_translations": null
}
],
"name_translations": null,
"description_translations": null,
"payment_requirement": "full",
"deposit_value": null
}
],
"currency": "USD",
"quote": {
"date": "2026-08-07",
"applies_pricing_rules": true,
"basis": "inventory[].quoted_price is the engine price of ONE unit of that tier for its event's own date and start time (this business's pricing rules applied; `date` here is the purchase date used for sale windows). Cart-level and customer-specific rules settle at booking; POST /book returns the authoritative total."
}
}jsonList Rentals
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| locale | string | Optional | Resolve name and description — across both rentals[] and add_on_rentals[] — into this language, omitting the *_translations maps. category has no translations and is unaffected. See Localization. |
| date | string | Optional | Booking date (YYYY-MM-DD) quoted_price should assume. Defaults to the business's current date. Malformed values return 400. See Pricing quotes. |
Example Request
curl https://reservly.io/api/public/luxe-salon/rentalsbash
Example Response
{
"rentals": [
{
"id": "a7b8c9d0-e1f2-3456-abcd-789012345678",
"name": "Private Styling Suite",
"description": "Fully equipped private suite for events, with styling chairs, mirrors, a wash station and its own entrance. Sleeps up to twelve guests comfortably.",
"category": "Spaces",
"price": 150,
"quoted_price": 172.5,
"price_type": "hourly",
"quantity": 2,
"image_url": "https://cdn.reservly.io/rentals/suite.jpg",
"min_rental_days": null,
"max_rental_days": null,
"min_rental_hours": 2,
"max_rental_hours": 8,
"name_translations": null,
"description_translations": null,
"payment_requirement": "deposit_percent",
"deposit_value": 25
}
],
"rental_upsells": [
{
"rental_id": "a7b8c9d0-e1f2-3456-abcd-789012345678",
"upsell_rental_id": "b8c9d0e1-f2a3-4567-bcde-890123456789",
"is_required": false,
"sort_order": 0
}
],
"currency": "USD",
"quote": {
"date": "2026-08-07",
"applies_pricing_rules": true,
"basis": "quoted_price is the engine-adjusted rate (price_type unchanged) for a booking on `date`, this business's pricing rules applied to the listed rate as a single-unit base. Duration, quantity, setup fees, duration discounts, and time/cart/customer-specific rules settle at booking; POST /book returns the authoritative total."
}
}jsonCreate a Booking
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| service_ids | uuid[] | Required | Array of service UUIDs to book |
| date | string | Required | Booking date in YYYY-MM-DD format |
| time | string | Required | Start time in HH:MM format (24-hour) |
| customer_name | string | Required | Full name of the customer |
| customer_email | string | Required | Customer email address |
| staff_id | uuid | Optional | Preferred staff member UUID |
| customer_phone | string | Optional | Customer phone number |
| form_data | object | Optional | Custom form field responses. Reserved keys beginning with an underscore are ignored on API-key requests — in particular `_consent`, which records marketing/SMS opt-in and can only be set by the customer on the business’s own booking page. |
| party_size | number | Optional | Number of guests (defaults to 1) |
| class_schedule_id | uuid | Optional | Class session UUID. For a class-based service the session is resolved from date + time and its seat is claimed automatically, and a time with no session is rejected — so you normally omit this. Send it only to disambiguate a date and time where two sessions of the same class start together. |
| source | string | Optional | Attribution source (e.g. "vapi", "n8n", "make"). Auto-tagged as "api" if omitted and request lacks browser headers. |
| preferred_provider | string | Optional | Preferred payment provider: "stripe" |
Request Body
{
"service_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"],
"date": "2026-04-15",
"time": "10:00",
"customer_name": "Jane Smith",
"customer_email": "jane@example.com",
"customer_phone": "+1-555-0123",
"staff_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"source": "n8n",
"party_size": 1
}jsonExample Request
curl -X POST https://reservly.io/api/public/luxe-salon/book \
-H "Content-Type: application/json" \
-H "Authorization: Bearer rsvly_YOUR_API_KEY" \
-d '{
"service_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"],
"date": "2026-04-15",
"time": "10:00",
"customer_name": "Jane Smith",
"customer_email": "jane@example.com"
}'bashExample Response
{
"success": true,
"booking": {
"id": "e5f6a7b8-c9d0-1234-abcd-567890123456",
"date": "2026-04-15",
"start_time": "10:00",
"end_time": "11:00",
"status": "pending",
"source": "n8n",
"services": [
{
"name": "Haircut & Style",
"duration_minutes": 60,
"price": 75
}
]
}
}jsonRate limit: 10 req/min (unauthenticated) | 100 req/min (authenticated)
Common Error Codes
| Status | Meaning | Example |
|---|---|---|
| 400 | Invalid parameters | Missing required field, bad date format, invalid UUID |
| 403 | Business pending deletion | The business account is scheduled for removal and no longer accepts bookings |
| 404 | Not found | Invalid slug, service ID does not exist, or resource is inactive |
| 409 | Booking conflict | The requested time slot is already booked or no longer available |
| 429 | Rate limited | Too many requests. Retry after the period indicated in the Retry-After header. |
| 500 | Server error | An unexpected error occurred. Contact support if it persists. |