Public Booking API
REST API for integrating availability, services, and bookings into external websites and applications. Uses API key authentication. All endpoints are prefixed with /api/v1.
Authentication
Every request must include a valid API key. You can pass it in either of two ways:
- X-API-Key header (recommended)
- Authorization: Bearer header
curl -H "X-API-Key: qv_pk_abc12345_0123456789abcdef0123456789abcdef" \
https://api.qvian.com/api/v1/servicescurl -H "Authorization: Bearer qv_pk_abc12345_0123456789abcdef0123456789abcdef" \
https://api.qvian.com/api/v1/servicesTo create an API key, see API Keys.
Response Format
All responses use a consistent JSON envelope.
Success Response
{
"success": true,
"data": { ... },
"meta": { "requestId": "abc123" }
}Error Response
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{ "message": "startDate must be a valid date string" }
]
},
"meta": { "requestId": "abc123" }
}Error Codes
| Code | HTTP Status | Description |
|---|---|---|
| VALIDATION_ERROR | 400 | Invalid request parameters or body |
| UNAUTHORIZED | 401 | Missing, invalid, revoked, or expired API key |
| FORBIDDEN | 403 | Key lacks required scope or origin not allowed |
| NOT_FOUND | 404 | Resource not found |
| RATE_LIMIT_EXCEEDED | 429 | Too many requests for this API key |
Endpoints
GET/api/v1/availability
Search availability across the organization or a specific business unit.
Scope: read
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| startDate | string | Yes | Start date (YYYY-MM-DD) |
| endDate | string | Yes | End date (YYYY-MM-DD) |
| adults | number | Yes | Number of adults (min 1) |
| children | number | Yes | Number of children (min 0) |
| businessUnitId | string | No | Filter to a specific business unit |
curl -H "X-API-Key: YOUR_KEY" \
"https://api.qvian.com/api/v1/availability?startDate=2026-03-01&endDate=2026-03-05&adults=2&children=0"{
"success": true,
"data": {
"businessUnitId": "bu-abc123",
"services": [
{
"serviceId": "svc-001",
"name": "Beach Villa",
"description": "Beachfront villa with ocean view",
"pricingModel": "PerNight",
"defaultPrice": 350.00,
"currency": "USD",
"bookingType": "Online",
"category": "Accommodation",
"durationMinutes": null,
"availableResources": [
{
"resourceId": "res-001",
"name": "Villa 1",
"code": "VIL-001",
"capacity": 4,
"status": "Available"
}
]
}
]
},
"meta": { "requestId": "abc123" }
}Only resources with no existing bookings in the requested date range are returned. Resources with any active reservation (reserved, confirmed, or checked-in) are excluded. If businessUnitId is omitted, the response groups results by business unit under a businessUnits array.
GET/api/v1/services
List active services for the organization.
Scope: read
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| businessUnitId | string | No | Filter by business unit |
| category | string | No | Filter by category name |
curl -H "X-API-Key: YOUR_KEY" \
"https://api.qvian.com/api/v1/services"{
"success": true,
"data": [
{
"id": "svc-001",
"name": "Beach Villa",
"description": "Beachfront villa with ocean view",
"serviceType": "Accommodation",
"pricingModel": "PerNight",
"defaultPrice": 350.00,
"currency": "USD",
"bookingType": "Online",
"category": "Accommodation",
"durationMinutes": null,
"businessUnitId": "bu-abc123",
"businessUnitName": "Island Resort",
"primaryImageUrl": null
}
],
"meta": { "requestId": "abc123" }
}GET/api/v1/services/:id
Get a single service by ID.
Scope: read
curl -H "X-API-Key: YOUR_KEY" \
"https://api.qvian.com/api/v1/services/svc-001"Returns the same service object format as the list endpoint. Returns 404 NOT_FOUND if the service does not exist or is not in the organization.
GET/api/v1/packages
List active packages for the organization.
Scope: read
curl -H "X-API-Key: YOUR_KEY" \
"https://api.qvian.com/api/v1/packages"{
"success": true,
"data": [
{
"id": "pkg-001",
"name": "Island Getaway",
"description": "3-night stay with diving and meals",
"packageType": "Accommodation",
"pricingMode": "Dynamic",
"basePrice": 1500.00,
"currency": "USD",
"minGuests": 2,
"maxGuests": 6,
"minNights": 3,
"maxNights": 7,
"validFrom": "2026-01-01",
"validTo": "2026-12-31",
"components": [
{
"id": "comp-001",
"serviceName": "Beach Villa",
"serviceType": "Accommodation",
"isOptional": false,
"quantity": 1
},
{
"id": "comp-002",
"serviceName": "Guided Dive",
"serviceType": "Activity",
"isOptional": true,
"quantity": 2
}
]
}
],
"meta": { "requestId": "abc123" }
}GET/api/v1/packages/:id/pricing
Calculate pricing for a package based on dates and guest count.
Scope: read
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| startDate | string | Yes | Start date (YYYY-MM-DD) |
| endDate | string | Yes | End date (YYYY-MM-DD) |
| guests | number | Yes | Number of guests (min 1) |
curl -H "X-API-Key: YOUR_KEY" \
"https://api.qvian.com/api/v1/packages/pkg-001/pricing?startDate=2026-03-01&endDate=2026-03-04&guests=2"{
"success": true,
"data": {
"packageId": "pkg-001",
"packageName": "Island Getaway",
"totalBasePrice": 1500.00,
"packagePrice": 1500.00,
"discountAmount": 0.00,
"guestCount": 2,
"nightCount": 3,
"components": [
{
"componentId": "comp-001",
"serviceName": "Beach Villa",
"basePrice": 1050.00,
"allocatedPrice": 1050.00,
"businessUnitId": "bu-abc123"
},
{
"componentId": "comp-002",
"serviceName": "Guided Dive",
"basePrice": 450.00,
"allocatedPrice": 450.00,
"businessUnitId": "bu-abc123"
}
]
},
"meta": { "requestId": "abc123" }
}POST/api/v1/reservations
Create a new reservation.
Scope: write
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| businessUnitId | string | Yes | Target business unit ID |
| customer | object | Yes | Customer details (see below) |
| startDate | string | Yes | Check-in date (YYYY-MM-DD) |
| endDate | string | Yes | Check-out date (YYYY-MM-DD) |
| guests | object | Yes | { adults: number, children: number } |
| totalAmount | number | Yes | Total price |
| primaryCurrency | string | Yes | Currency code (e.g. "USD") |
| selectedPackage | string | No | Package ID |
| selectedServices | array | No | Individual services to book (see below) |
| selectedAddons | array | No | Array of addon IDs |
| packageComponentResources | array | No | Per-component resource assignments for package bookings (see below) |
| discount | object | No | Discount to apply (see below) |
| notes | string | No | Booking notes or special requests |
Customer object:
| Field | Type | Required |
|---|---|---|
| firstName | string | Yes |
| lastName | string | Yes |
| string | Yes | |
| phone | string | No |
| title | string | No |
selectedServices[] object:
| Field | Type | Required | Description |
|---|---|---|---|
| serviceId | string | Yes | Service ID |
| businessUnitId | string | No | Business unit for cross-BU bookings |
| startDate | string | Yes | Service start date (YYYY-MM-DD) |
| endDate | string | Yes | Service end date (YYYY-MM-DD) |
| quantity | number | No | Total guest count. For accommodation: total number of guests staying (e.g. 2 adults + 1 child = 3). For activities: number of seats/participants. Defaults to 1. |
| selectedResourceIds | string[] | No | Specific resource IDs (e.g. room IDs) |
| resourceScheduleId | string | No | Link to a specific time slot for activities (diving, spa, etc.) |
| serviceDetails | object | No | Service metadata (booking type, departure times, meal selections, etc.) |
packageComponentResources[] object:
| Field | Type | Required | Description |
|---|---|---|---|
| componentId | string | Yes | Package component ID (from GET /packages) |
| selectedResourceIds | string[] | Yes | Resource IDs to assign to this component |
| resourceScheduleId | string | No | Time slot for activity components |
| scheduleSelections | array | No | Multiple schedule+resource pairs: [{ scheduleId, resourceId }] |
| startDate | string | No | Override start date for this component (YYYY-MM-DD) |
| endDate | string | No | Override end date for this component (YYYY-MM-DD) |
discount object:
| Field | Type | Required | Description |
|---|---|---|---|
| type | string | Yes | "amount" (fixed) or "percent" |
| value | number | Yes | Discount value (e.g. 50 for $50 off or 10 for 10%) |
| reason | string | No | Reason for the discount |
curl -X POST -H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"businessUnitId": "bu-abc123",
"customer": {
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com"
},
"startDate": "2026-03-01",
"endDate": "2026-03-05",
"guests": { "adults": 2, "children": 0 },
"selectedPackage": "pkg-001",
"packageComponentResources": [
{
"componentId": "comp-001",
"selectedResourceIds": ["res-villa-3"]
},
{
"componentId": "comp-002",
"selectedResourceIds": ["res-dive-boat-1"],
"resourceScheduleId": "sched-morning-dive"
}
],
"discount": { "type": "percent", "value": 10, "reason": "Returning guest" },
"totalAmount": 1350.00,
"primaryCurrency": "USD"
}' \
"https://api.qvian.com/api/v1/reservations"curl -X POST -H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"businessUnitId": "bu-abc123",
"customer": {
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"phone": "+1234567890"
},
"startDate": "2026-03-01",
"endDate": "2026-03-05",
"guests": { "adults": 2, "children": 0 },
"selectedPackage": "pkg-001",
"totalAmount": 1500.00,
"primaryCurrency": "USD",
"notes": "Late arrival, after 10 PM"
}' \
"https://api.qvian.com/api/v1/reservations"{
"success": true,
"data": {
"reference": "RES-000123",
"status": "Draft",
"startDate": "2026-03-01",
"endDate": "2026-03-05",
"adults": 2,
"children": 0,
"totalAmount": 1500.00,
"primaryCurrency": "USD",
"createdAt": "2026-02-28T10:00:00.000Z"
},
"meta": { "requestId": "abc123" }
}Reservations are created as Draft
GET/api/v1/reservations/:reference
Get reservation details by reference number.
Scope: read
curl -H "X-API-Key: YOUR_KEY" \
"https://api.qvian.com/api/v1/reservations/RES-000123"{
"success": true,
"data": {
"reference": "RES-000123",
"status": "Confirmed",
"startDate": "2026-03-01",
"endDate": "2026-03-05",
"adults": 2,
"children": 0,
"totalAmount": 1500.00,
"subtotal": 1500.00,
"totalDiscount": 0.00,
"totalTax": 0.00,
"primaryCurrency": "USD",
"paymentStatus": "Pending",
"customer": {
"name": "John Doe",
"email": "john@example.com",
"phone": "+1234567890"
},
"items": [
{
"id": "item-001",
"itemType": "Package",
"startDate": "2026-03-01T00:00:00.000Z",
"endDate": "2026-03-05T00:00:00.000Z",
"amount": 1500.00,
"status": "Unserved",
"businessUnit": "Island Resort",
"services": [
{ "serviceName": "Beach Villa", "quantity": 1 },
{ "serviceName": "Guided Dive", "quantity": 2 }
]
}
],
"createdAt": "2026-02-28T10:00:00.000Z"
},
"meta": { "requestId": "abc123" }
}Integration Guide
This section explains how to design a booking website or application that gets the most out of the Qvian Suite reservation system. The API supports everything from simple one-room bookings to complex multi-property packages with activity scheduling and discounts.
Recommended Booking Flow
A well-designed booking website should follow this sequence of API calls to build a complete reservation:
- Fetch services and packages — Call
GET /api/v1/servicesandGET /api/v1/packagesto populate your catalog. Cache these; they change infrequently. - Check availability — When the user selects dates and guest count, call
GET /api/v1/availabilityto get available services and specific resources (rooms, boats, etc.). - Calculate package pricing — If the user selects a package, call
GET /api/v1/packages/:id/pricingwith their dates and guest count. This returns the exact price and per-component breakdown with component IDs you will need for resource customization. - Let the user customize — Show available rooms, time slots, and optional add-ons. Map user selections to the fields described below.
- Create the reservation — Call
POST /api/v1/reservationswith all collected data. - Show confirmation — Display the returned reference number (e.g.
RES-260301-001). Optionally pollGET /api/v1/reservations/:referenceto show status updates.
Booking Scenarios
The reservation endpoint supports several booking patterns. You can combine them in a single request.
1. Package Booking (Most Common)
Packages bundle accommodation, activities, and meals into a single priced product. Set selectedPackage to the package ID. The server creates one reservation item per package component and handles pricing automatically.
{
"businessUnitId": "bu-resort",
"customer": { "firstName": "Jane", "lastName": "Smith", "email": "jane@example.com" },
"startDate": "2026-03-01",
"endDate": "2026-03-05",
"guests": { "adults": 2, "children": 1 },
"selectedPackage": "pkg-island-getaway",
"totalAmount": 2400.00,
"primaryCurrency": "USD"
}Without packageComponentResources, the system auto-assigns the best available room and default time slots. To let the user pick their room or activity time, see the Package Customization section below.
2. A La Carte Services
For bookings without a package, use selectedServices to pick individual services. Create one entry per service (not one per room) — put all room IDs in selectedResourceIds and set quantity to the total guest count for that service.
{
"businessUnitId": "bu-resort",
"customer": { "firstName": "Jane", "lastName": "Smith", "email": "jane@example.com" },
"startDate": "2026-03-01",
"endDate": "2026-03-05",
"guests": { "adults": 4, "children": 0 },
"selectedServices": [
{
"serviceId": "svc-beach-villa",
"startDate": "2026-03-01",
"endDate": "2026-03-05",
"quantity": 4,
"selectedResourceIds": ["room-villa-1", "room-villa-2"]
},
{
"serviceId": "svc-guided-dive",
"startDate": "2026-03-03",
"endDate": "2026-03-03",
"quantity": 4,
"selectedResourceIds": ["vessel-1"],
"resourceScheduleId": "sched-morning-8am"
}
],
"totalAmount": 3200.00,
"primaryCurrency": "USD"
}3. Mixed: Package + Extra Services
You can combine a package with additional a la carte services and add-ons in a single reservation. The package components are created first, then the extra services, then add-ons. If a discount is applied, it distributes proportionally across all items.
{
"businessUnitId": "bu-resort",
"customer": { "firstName": "Jane", "lastName": "Smith", "email": "jane@example.com" },
"startDate": "2026-03-01",
"endDate": "2026-03-05",
"guests": { "adults": 2, "children": 0 },
"selectedPackage": "pkg-island-getaway",
"selectedServices": [
{
"serviceId": "svc-spa-massage",
"startDate": "2026-03-02",
"endDate": "2026-03-02",
"quantity": 2,
"resourceScheduleId": "sched-spa-2pm"
}
],
"selectedAddons": ["addon-airport-transfer", "addon-honeymoon-setup"],
"discount": { "type": "percent", "value": 10, "reason": "Returning guest" },
"totalAmount": 2700.00,
"primaryCurrency": "USD"
}How Pricing Works
While you must send a totalAmount, the server recalculates the actual total from the selected services, package pricing, and discounts. The value you send serves as a reference but the authoritative price comes from the server. Use GET /api/v1/packages/:id/pricing and the service defaultPrice from the services endpoint to calculate accurate totals on your end.
Services use different pricing models depending on their type:
| Pricing Model | Calculation | Typical Use |
|---|---|---|
| PerNight | unit price x rooms x nights | Accommodation (rooms, villas) |
| PerPerson | unit price x participants | Activities (diving, yoga, excursions) |
| Fixed | flat unit price | Transfers, one-time services |
| PerHour | unit price x hours | Equipment rental, guided tours |
| PerDay | unit price x days | Equipment rental, day passes |
| PerTrip | flat unit price | Excursions, transfers |
Rooms and Resource Selection
Resources are the physical things being booked — rooms, villas, dive boats, spa treatment rooms, yoga decks, etc. The availability endpoint returns availableResources for each service, which gives you the resource IDs to use.
Resource selection is optional but recommended
selectedResourceIds, the system auto-assigns the best available resource. However, letting the user choose (e.g. "Villa 3 — Beachfront" vs "Villa 7 — Garden View") creates a much better booking experience. Use the availability endpoint to show what's available.For accommodation (PerNight services), each resource ID in selectedResourceIds represents one room. Use the capacity field from the availability response to determine how many rooms you need (e.g. if each villa has capacity 4, then 6 guests need 2 villas). Create a single selectedServices entry with all room IDs and the total guest count:
{
"serviceId": "svc-deluxe-room",
"startDate": "2026-03-01",
"endDate": "2026-03-05",
"quantity": 4,
"selectedResourceIds": ["room-101", "room-102"]
}One entry per service — quantity = total guests
selectedServices entries for each room. Instead, create one entry per service with all room IDs in selectedResourceIds and quantity set to the total guest count. The system automatically distributes guests across the rooms using each service's capacity configuration. For example, 4 adults across 2 rooms = quantity: 4 with selectedResourceIds: ["room-101", "room-102"].Activity Time Slots
Activities like diving, spa treatments, and excursions often run on a schedule (e.g. "Morning Dive at 8:00 AM", "Afternoon Spa at 2:00 PM"). The resourceScheduleId field links a booking to a specific time slot. Without it, the system creates a generic period allocation instead of a precise scheduled booking — staff won't see which departure or session the guest chose.
To build a time slot picker, use the availability endpoint to discover services and resources, then let the user pick a slot. The schedule ID comes from your service catalog or availability data.
{
"serviceId": "svc-guided-dive",
"startDate": "2026-03-03",
"endDate": "2026-03-03",
"quantity": 4,
"selectedResourceIds": ["dive-boat-1"],
"resourceScheduleId": "sched-morning-8am"
}The quantity field represents seats/participants, not resource count. If 4 guests book a dive, set quantity: 4 — the system allocates 4 seats on the specified vessel.
Service Details (Metadata)
The serviceDetails field is a freeform JSON object attached to each service booking. It is stored as-is and displayed to staff in the reservation detail view. Use it to capture any service-specific information that staff need to fulfill the booking.
// Diving activity
"serviceDetails": {
"certificationLevel": "Advanced Open Water",
"equipmentNeeded": ["BCD", "Regulator"],
"departureTime": "08:00"
}
// Spa treatment
"serviceDetails": {
"treatmentPreference": "Deep Tissue",
"therapistGender": "Female",
"allergies": "None"
}
// Meal plan
"serviceDetails": {
"dietaryRequirements": ["Vegetarian", "Gluten-free"],
"mealPlan": "Half Board"
}
// Transfer
"serviceDetails": {
"flightNumber": "QR 674",
"arrivalTime": "14:30",
"pickupLocation": "Velana International Airport"
}Package Customization
Packages contain multiple components (e.g. a villa + 2 dives + meals). By default, the system auto-assigns resources to each component. Use packageComponentResources to let the user choose which room they want, which dive slot they prefer, etc.
The component IDs come from the GET /api/v1/packages response, where each package has a components[] array with id fields.
{
"selectedPackage": "pkg-island-getaway",
"packageComponentResources": [
{
"componentId": "comp-villa",
"selectedResourceIds": ["room-villa-3"]
},
{
"componentId": "comp-dive",
"selectedResourceIds": ["dive-boat-1"],
"resourceScheduleId": "sched-morning-8am",
"startDate": "2026-03-02",
"endDate": "2026-03-02"
},
{
"componentId": "comp-second-dive",
"selectedResourceIds": ["dive-boat-2"],
"resourceScheduleId": "sched-afternoon-2pm",
"startDate": "2026-03-04",
"endDate": "2026-03-04"
}
]
}Key points:
- You only need to include components the user wants to customize. Omitted components use auto-assignment.
- Use
startDate/endDateon a component to override when that activity happens within the stay. - For components with multiple schedule slots (e.g. a dive that includes morning briefing + afternoon dive), use
scheduleSelectionsinstead of a singleresourceScheduleId.
{
"componentId": "comp-full-day-dive",
"selectedResourceIds": [],
"scheduleSelections": [
{ "scheduleId": "sched-briefing-7am", "resourceId": "classroom-1" },
{ "scheduleId": "sched-dive-9am", "resourceId": "dive-boat-1" }
],
"startDate": "2026-03-03",
"endDate": "2026-03-03"
}Discounts
Apply a discount to the entire reservation using the discount field. The server distributes the discount proportionally across all reservation items (both package components and a la carte services). Each item's discount is capped at its own amount — no item goes below zero.
| Type | Example | Behavior |
|---|---|---|
| "amount" | { "type": "amount", "value": 200 } | $200 off the total, distributed proportionally |
| "percent" | { "type": "percent", "value": 15 } | 15% off each item proportionally |
The reason field is optional but recommended — it appears in the staff reservation detail view (e.g. "Returning guest", "Early bird promo", "Corporate rate").
Cross-Business-Unit Bookings
If the organization has multiple business units (e.g. a resort and a dive center), a single reservation can span both. The top-level businessUnitId is the primary/receiving property. For a la carte services, use businessUnitId on each service to specify which BU provides it. For packages, cross-BU is handled automatically based on which BU each package component belongs to.
{
"businessUnitId": "bu-resort",
"selectedServices": [
{
"serviceId": "svc-beach-villa",
"businessUnitId": "bu-resort",
"startDate": "2026-03-01",
"endDate": "2026-03-05",
"selectedResourceIds": ["room-villa-1"]
},
{
"serviceId": "svc-guided-dive",
"businessUnitId": "bu-dive-center",
"startDate": "2026-03-03",
"endDate": "2026-03-03",
"quantity": 2,
"resourceScheduleId": "sched-morning-8am"
}
]
}The system handles inter-business-unit financial settlements automatically when payment is processed by staff.
Customer Matching
The system automatically matches customers by email address. If a guest has booked before, their existing customer profile is linked to the new reservation — staff see the full history. You can also pass tradingPartnerId in the customer object to explicitly link to a known customer record. If no match is found, a new customer profile is created automatically.
Design Tips for Building a Booking Website
Cache your catalog
Services and packages change infrequently. Fetch them once and cache for hours or days. Only availability needs to be checked in real-time.
Show available rooms, not just room types
The availability endpoint returns specific resources with names (e.g. "Ocean Villa 3"). Showing named rooms creates a premium experience and ensures the guest gets exactly what they chose.
Let guests pick activity time slots
Always pass resourceScheduleId for activities. Without it, staff see a generic booking with no time information and have to follow up manually.
Use packages when available
Packages handle pricing, revenue allocation, and component bundling automatically. If the property offers packages, prefer them over manually assembling individual services.
Pass serviceDetails generously
Any information that helps staff prepare — flight numbers, dietary needs, certification levels, special occasions — should go in serviceDetails. It saves back-and-forth communication.
Use YYYY-MM-DD dates, never ISO timestamps
All date fields expect YYYY-MM-DD format. Do not use .toISOString() in JavaScript — it can shift dates due to timezone conversion. Use a simple date formatter instead.
Poll for status updates
Reservations are created as Draft. If you want to show guests when their booking is confirmed, poll GET /api/v1/reservations/:reference periodically and update your UI when the status changes.
Complete Example: Full-Featured Reservation
This example uses every capability — package with room customization, activity time slots, extra services, add-ons, discount, service details, and notes:
{
"businessUnitId": "bu-island-resort",
"customer": {
"firstName": "Sarah",
"lastName": "Chen",
"email": "sarah.chen@example.com",
"phone": "+1-555-0123",
"title": "Ms"
},
"startDate": "2026-04-10",
"endDate": "2026-04-15",
"guests": { "adults": 2, "children": 1 },
"selectedPackage": "pkg-family-island-escape",
"packageComponentResources": [
{
"componentId": "comp-family-villa",
"selectedResourceIds": ["villa-beachfront-2"]
},
{
"componentId": "comp-snorkel-trip",
"selectedResourceIds": ["snorkel-boat-1"],
"resourceScheduleId": "sched-snorkel-morning",
"startDate": "2026-04-12",
"endDate": "2026-04-12"
}
],
"selectedServices": [
{
"serviceId": "svc-sunset-cruise",
"startDate": "2026-04-13",
"endDate": "2026-04-13",
"quantity": 3,
"resourceScheduleId": "sched-cruise-5pm",
"serviceDetails": {
"dietaryRequirements": ["No shellfish"],
"celebration": "Wedding anniversary"
}
}
],
"selectedAddons": ["addon-airport-transfer-roundtrip"],
"discount": {
"type": "percent",
"value": 10,
"reason": "Returning guest — 3rd visit"
},
"totalAmount": 4320.00,
"primaryCurrency": "USD",
"notes": "Arriving on seaplane at 3 PM. Please arrange golf cart pickup at jetty."
}Rate Limiting
Each API key has a configurable rate limit (default: 60 requests per minute). When the limit is exceeded, the API returns a 429 response with the RATE_LIMIT_EXCEEDED error code.
To increase the limit, create a new API key with a higher Rate Limit value (up to 1000 requests per minute). See API Keys for instructions.
Troubleshooting
401 Unauthorized
Cause: API key is missing, invalid, revoked, or expired.
Fix: Verify the key is included in the X-API-Key or Authorization header. Check the key status in Settings → API Keys.
403 Forbidden
Cause: Key lacks the required scope, or the request origin is not in the allowed origins list.
Fix: Ensure the key has the correct scope (Read for GET endpoints, Write for POST). If calling from a browser, add the domain to the key's allowed origins.
429 Too Many Requests
Cause: Rate limit exceeded for this API key.
Fix: Wait and retry, or create a new key with a higher rate limit.
Reservation Created but Not Confirmed
Cause: This is expected behavior.
Explanation: API-created reservations have Draft status. Staff review and confirm them in the Qvian Suite application. Use the GET /api/v1/reservations/:reference endpoint to check the current status.
Related: API Keys, Reservations, Service Management.