/Help Center
GuideDeveloper reference

Booking Engine API

REST API for embedding HotelBee's booking flow in third-party apps. Covers room availability, room types, currencies, reservations, promo codes, OTA price comparison, and the guest portal (login, profile, reservations, invoices, orders, online check-in).

Verified Jun 5, 202628 min read

Last updated: June 5, 2026

Overview

The HotelBee Booking Engine API allows third-party applications to integrate with HotelBee's booking system. This API enables you to:

  • Retrieve room type catalogs, availability, and pricing
  • Search for available rooms across date ranges
  • Create reservations directly in HotelBee
  • Validate promo codes for discounts
  • Compare OTA channel prices
  • Power a guest portal with self-service check-in, invoice viewing, and order history

This is a RESTful API that returns JSON responses and uses standard HTTP response codes.

Base URL

All API requests should be made to:

https://app.hotelbee.co/api/booking-engine

Authentication

The Booking Engine API uses two authentication methods depending on the endpoint type.

Public Booking Engine Authentication

All public booking engine endpoints require API key authentication.

Include these headers with every request:

  • Authorization (string, required): Bearer {api_key} — your API key (contact support@hotelbee.co to obtain)
  • Property (string, required): Your property name
  • Username (string, required): Username associated with your API key
  • Content-Type (string, required for POST): application/json for POST requests
The Property header is required on *every* endpoint — public booking
engine and guest portal alike. A request without it fails with 400
("Property Missing"). Do not omit it, even on guest-token requests.

Example Authentication Headers

json
{
  "Authorization": "Bearer your-api-key-here",
  "Property": "myhotel",
  "Username": "booking_engine"
}

Guest Portal Authentication

Guest portal endpoints (except login and some public ones) require an additional JWT token obtained via the /guest-portal/login endpoint. This is sent alongside the API key headers — guest portal requests carry both the API key (Authorization, Property, Username) and the guest token.

  • X-Guest-Token (string, required): JWT token obtained from /guest-portal/login
  • Property (string, required): Your property name
  • Plus the standard API key headers (Authorization, Username).
Send the guest token only in X-Guest-Token. The Authorization header
always carries your API key, so the guest token must go in X-Guest-Token
it is the only header that works for it.

Token Details:

  • Expiry: 7 days. Treat the token as an opaque string.

What your key can do

Your key is scoped to the property (or properties) it was issued for. A few things to know:

  • Property scope. A request whose Property header is not one your key was issued for is rejected with 403 Forbidden.
  • Client search is not available to integration keys — GET /clients/search returns 403.
  • Discounts are applied by passing a promoCode (see Create Reservation).
  • Payments go through Stripe (/create-payment-intent) or, if enabled for your key, your own payment gateway (externalPayment). Sending raw card details is not supported and returns 400 raw_card_not_supported.

To obtain a key, contact support@hotelbee.co with your company name, the property (or properties) you'll integrate, and whether you process payments on your own gateway.

Rate Limiting

Requests are rate limited per property and client IP. Exceeding a limit returns HTTP 429:

json
{ "error": "Too many requests. Please try again shortly." }

| Endpoints | Limit | |---|---| | Reads (/availabilities, /roomtypes, /config, /currencies, /ota-prices, /guestcategories, /clients/search) and /quote | 120 / minute | | /reservations, /create-payment-intent | 10 / minute | | /promocode/validate | 15 / minute | | /guest-portal/login | 10 / 5 minutes |

Error Handling

The API uses standard HTTP status codes:

  • 200 OK — Request successful
  • 400 Bad Request — Missing required data or validation error
  • 401 Unauthorized — Authentication failed
  • 403 Forbidden — Your key is not authorized for this property, or for a feature it doesn't have access to
  • 404 Not Found — Resource or property not found
  • 429 Too Many Requests — Rate limit exceeded (see Rate Limiting)
  • 500 Internal Server Error — Server error

Error responses use this format, where error is a stable code and message is optional human-readable text:

json
{
  "error": "code",
  "message": "optional human-readable text"
}

Public Endpoints

Get Availability

Retrieves room availability and pricing for a given date range. Use this to show available room types and their daily rates on your booking widget.

GET /availabilities — Fetches daily room availability and pricing for a date range. Auth required (API key).

Query Parameters

  • startDate (string, required): Start date in YYYY-MM-DD format
  • endDate (string, required): End date in YYYY-MM-DD format

Example Request

bash
curl -X GET "https://app.hotelbee.co/api/booking-engine/availabilities?startDate=2026-04-01&endDate=2026-04-05" \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine"

Response 200 — Success

json
{
  "startDate": "2026-04-01",
  "endDate": "2026-04-05",
  "roomtypes": {
    "roomtype_2026-01-15T10:00:00Z": {
      "2026-04-01": {
        "price": 100,
        "includedCapacity": 2,
        "capacity": [
          { "name": "Adults", "price": 25 },
          { "name": "Children", "price": 15 }
        ]
      },
      "2026-04-02": {
        "price": 100,
        "includedCapacity": 2,
        "capacity": [
          { "name": "Adults", "price": 25 },
          { "name": "Children", "price": 15 }
        ]
      }
    }
  },
  "rates": {
    "rate_2026-01-20T08:00:00Z": {
      "roomtype_2026-01-15T10:00:00Z": {
        "2026-04-01": {
          "price": 90,
          "includedCapacity": 2,
          "capacity": [
            { "name": "Adults", "price": 20 }
          ]
        }
      }
    }
  },
  "roomtypeQuantities": {
    "roomtype_2026-01-15T10:00:00Z": 5
  }
}

Response 400 — Missing Parameters

json
{
  "error": "startDate and endDate are required"
}

Response Fields

  • startDate (string): Requested start date
  • endDate (string): Requested end date
  • roomtypes (object): Map of room type ID → date → pricing (base rate pricing per day)
  • rates (object): Map of rate ID → room type ID → date → pricing (rate-specific pricing per day)
  • roomtypeQuantities (object): Map of room type ID → available room count

Pricing Object

  • price (number): Base price per night for included capacity
  • includedCapacity (integer): Number of guests included in the base price
  • capacity (array): Extra guest charge tiers (name + price per additional guest)

Use Cases

  • Display a date-range availability calendar
  • Show pricing per night per room type
  • Calculate total stay cost including extra guest charges
  • Check room availability before creating a reservation

Get Room Types

Retrieves the complete room type catalog including details, amenities, images, bed types, services, and booking engine rates.

GET /roomtypes — Fetches all room types with full details, services, and rates. Auth required (API key). Optionally accepts a guest token.

Personalized rates: This endpoint runs optional guest authentication. If
you also send a valid X-Guest-Token, the response includes client-specific
(private) rates targeted at that guest — i.e. rates whose availableTo matches
the guest's client ID — in addition to the public rates. Those private rates
are flagged with isClientSpecificRate: true. Without a guest token, only
public rates (availableTo: "all") are returned. An invalid or expired token
is ignored rather than rejected, so the call still succeeds with public rates.

Example Request

bash
curl -X GET https://app.hotelbee.co/api/booking-engine/roomtypes \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine"

Response 200 — Success

json
[
  {
    "id": "roomtype_2026-01-15T10:00:00Z",
    "name": "Deluxe Room",
    "description": "Spacious room with sea view",
    "capacity": [
      { "name": "Adults", "price": 25 },
      { "name": "Children", "price": 15 }
    ],
    "maxCapacity": 4,
    "includedCapacity": 2,
    "price": 100,
    "images": [
      "https://storage.blob.core.windows.net/hotel/room1.jpg"
    ],
    "amenities": ["WiFi", "AC", "Mini Bar"],
    "meals": ["breakfast"],
    "bedtypes": [
      {
        "id": "bedtype_001",
        "name": "Double Bed",
        "capacity": 2,
        "quantity": 1
      }
    ],
    "bookingEngineServices": [
      {
        "id": "service_spa_001",
        "name": "Spa Access",
        "code": "SPA001",
        "calculationType": "per_night",
        "frequency": "nightly",
        "price": 25,
        "taxData": [
          {
            "taxId": "tax_001",
            "isIncluded": false,
            "tax": { "rate": 20, "category": "VAT" }
          }
        ]
      }
    ],
    "rates": [
      {
        "id": "rate_spring_001",
        "name": "Spring Rate",
        "roomTypes": ["roomtype_2026-01-15T10:00:00Z"],
        "meals": ["breakfast"],
        "maxCapacity": 4,
        "includedCapacity": 2,
        "minimumNights": 1,
        "maximumNights": 30,
        "minimumAdvance": 0,
        "maximumAdvance": 365,
        "availableTo": "all",
        "services": [
          {
            "serviceId": "service_spa_001",
            "isPriceIncluded": true,
            "isInvoiceItem": false,
            "service": {}
          }
        ],
        "lunchCalculation": null
      }
    ]
  }
]

Response Fields

  • id (string): Unique room type identifier
  • name (string): Room type display name
  • description (string): Room type description
  • capacity (array): Extra guest charge tiers
  • maxCapacity (integer): Maximum guest capacity
  • includedCapacity (integer): Guests included in base price
  • price (number): Default base price
  • images (array): Image URLs
  • amenities (array): List of amenity names
  • meals (array): Included meal plans
  • bedtypes (array): Bed configurations with capacity
  • bookingEngineServices (array): Optional add-on services with pricing and tax data
  • rates (array): Booking engine rates (only rates with bookingEngineRate: true)

Rate Object Fields

  • minimumNights (integer): Minimum stay length required
  • maximumNights (integer): Maximum stay length allowed
  • minimumAdvance (integer): Minimum days in advance to book
  • maximumAdvance (integer): Maximum days in advance to book
  • availableTo (string): Who can book this rate. "all" for public rates, or a client ID for a private rate targeted at a specific guest
  • isClientSpecificRate (boolean): true only when a guest token was sent and this rate is private to that guest. Absent/false for public rates
  • cancelationPolicyId (string | null): ID of the cancellation policy attached to the rate, if any
  • cancelationPolicy (object | null): The resolved cancellation policy for the rate, if one is attached
  • services (array): Services included/available with this rate

Get Configuration

Retrieves all booking engine configuration including branding, footer, language settings, property metadata, terms, and payment methods.

GET /config — Fetches booking engine configuration and branding. Auth required (API key).

Example Request

bash
curl -X GET https://app.hotelbee.co/api/booking-engine/config \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine"

Response 200 — Success

json
{
  "branding": {
    "hotelTitle": "Hotel Riviera",
    "showHotelTitle": true,
    "logo": "https://storage.blob.core.windows.net/hotel/logo.png",
    "headerColor": "#1a237e",
    "buttonColor": "#ff6f00",
    "backgroundImage": "https://storage.blob.core.windows.net/hotel/bg.jpg",
    "font": "Roboto",
    "showPropertyAddress": true
  },
  "footer": {
    "enabled": true,
    "text": "© 2026 Hotel Riviera",
    "links": [],
    "contactEmail": "info@hotelriviera.com",
    "contactPhone": "+355 69 123 4567",
    "aboutUsText": "A luxury seaside hotel...",
    "locationLink": "https://maps.google.com/?q=..."
  },
  "language": {
    "enabledLanguages": ["en", "sq", "it"],
    "defaultLanguage": "sq",
    "showFlags": true
  },
  "propertyMeta": {
    "name": "Hotel Riviera",
    "address": "Rruga e Plazhit",
    "city": "Durrës",
    "region": "Albania",
    "zip": "2001",
    "country": "AL",
    "email": "info@hotelriviera.com",
    "website": "https://hotelriviera.com",
    "phone": "+355 69 123 4567"
  },
  "terms": {
    "cancellationTerms": [],
    "bookingTerms": [],
    "confirmationMessage": [],
    "showCancellationSummary": true
  },
  "paymentMethods": [
    {
      "id": "method_card_001",
      "name": "Credit Card",
      "method": "card",
      "instructions": "We accept Visa and Mastercard"
    },
    {
      "id": "method_bank_001",
      "name": "Bank Transfer",
      "method": "bank",
      "instructions": "Transfer to IBAN: AL00..."
    }
  ],
  "guestPortalInformation": {
    "locationLink": "https://maps.google.com/?q=...",
    "wifiName": "HotelRiviera_WiFi",
    "wifiPassword": "welcome2026",
    "goodToKnow": "Breakfast is served 7-10am in the main restaurant"
  }
}

Response Fields

  • branding (object): UI branding settings (logo, colors, fonts, background)
  • footer (object): Footer content (contact info, about text, links)
  • language (object): Language settings (enabled languages, default, flags)
  • propertyMeta (object): Hotel property details (name, address, contact)
  • terms (object): Booking and cancellation terms
  • paymentMethods (array): Active payment methods (filtered to bank, cash, card only)
  • guestPortalInformation (object): Guest portal information (WiFi, location, notes)

Important Notes

  • Payment Methods: Only methods with isActive: true and method type in [bank, cash, card] are returned
  • Branding: Use these values to style your booking engine UI to match the hotel's brand

Get Currencies

Retrieves all active currencies and identifies the default currency for the property.

GET /currencies — Fetches active currencies and default currency. Auth required (API key).

Example Request

bash
curl -X GET https://app.hotelbee.co/api/booking-engine/currencies \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine"

Response 200 — Success

json
{
  "currencies": [
    {
      "id": "currency_001",
      "currency": "ALL",
      "currencyName": "Albanian Lek",
      "rate": 1.0,
      "active": true
    },
    {
      "id": "currency_002",
      "currency": "EUR",
      "currencyName": "Euro",
      "rate": 0.0089,
      "active": true
    }
  ],
  "defaultCurrency": {
    "id": "currency_001",
    "currency": "ALL",
    "currencyName": "Albanian Lek",
    "rate": 1.0,
    "active": true
  }
}

Response Fields

  • currencies (array): All active currencies with exchange rates
  • defaultCurrency (object): The property's default currency

Currency Object

  • id (string): Currency identifier
  • currency (string): ISO 4217 currency code (e.g., "ALL", "EUR", "USD")
  • currencyName (string): Full currency name
  • rate (number): Exchange rate relative to default currency
  • active (boolean): Whether currency is active

Get Guest Categories

Retrieves the property's guest categories (e.g. Adults, Children, Infants) with their age ranges. Use these to build guest selectors and to label the capacity tiers returned by /availabilities and /roomtypes.

GET /guestcategories — Fetches guest categories sorted by priority. Auth required (API key).

Example Request

bash
curl -X GET https://app.hotelbee.co/api/booking-engine/guestcategories \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine"

Response 200 — Success

json
[
  {
    "id": "guestcategories_001",
    "name": "Adults",
    "from": 13,
    "to": null,
    "description": "Ages 13 and up",
    "priorityOrder": 1
  },
  {
    "id": "guestcategories_002",
    "name": "Children",
    "from": 2,
    "to": 12,
    "description": "Ages 2–12",
    "priorityOrder": 2
  }
]

Response Fields

  • id (string): Guest category identifier
  • name (string): Display name (matches the name used in capacity tiers)
  • from (integer | null): Minimum age for the category
  • to (integer | null): Maximum age for the category (null = no upper bound)
  • description (string | null): Optional description
  • priorityOrder (integer | null): Sort order; the lowest-priority category is treated as the primary guest type
Results are sorted by priorityOrder, then by from, then by name.

Get a Quote

Returns a server-authoritative price for a selection without any side effects — no client, reservation, or payment is created. /quote and /reservations share the exact same pricing pipeline, so the quoted amount cannot drift from what the reservation is created with for an unchanged selection.

This is the first step of the custom-gateway payment flow: quote the amount, charge it on your own gateway, then create the reservation reporting that charge.

POST /quote — Prices a selection. Auth required (API key). Content-Type: application/json.

Request Parameters

The body is the same selection shape as POST /reservations:

  • checkIn (string, required): Check-in date (YYYY-MM-DD)
  • checkOut (string, required): Check-out date (YYYY-MM-DD)
  • roomTypes (array, required): Room type bookings (see roomTypes[] Object)
  • promoCode (string, optional): Promo code to validate and apply server-side

Example Request

bash
curl -X POST https://app.hotelbee.co/api/booking-engine/quote \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "Content-Type: application/json" \
  -d '{
    "checkIn": "2026-07-01",
    "checkOut": "2026-07-04",
    "roomTypes": [
      { "roomTypeId": "roomtype_2026-01-15T10:00:00Z", "rateId": "rate_spring_001", "adults": 2, "children": 0, "quantity": 1 }
    ],
    "promoCode": "SUMMER10"
  }'

Response 200 — Success

json
{
  "amount": 270.00,
  "currency": "EUR",
  "description": "Reservation 2026-07-01 → 2026-07-04 • 1 room, 3 nights",
  "checkIn": "2026-07-01",
  "checkOut": "2026-07-04",
  "roomCount": 1,
  "nights": 3,
  "discountApplied": { "type": "percentage", "value": 10 }
}

Response Fields

  • amount (number): The authoritative total to charge
  • currency (string): Currency code for the amount
  • description (string): Human-readable summary (suitable as a gateway charge description)
  • checkIn / checkOut (string): Echoed dates
  • roomCount (integer): Number of rooms in the selection
  • nights (integer): Number of nights
  • discountApplied (object | null): The discount applied from promoCode, if any

Create Payment Intent

Creates a Stripe PaymentIntent for an online booking payment, returning a clientSecret your front end uses to confirm the card payment with Stripe.js. Only works for properties that have Stripe Connect set up and booking-engine payments enabled.

POST /create-payment-intent — Creates a Stripe PaymentIntent for a booking. Auth required (API key). Content-Type: application/json.

Request Parameters

  • amount (number, required): Intended charge amount in major currency units (e.g. 150.00). Must be greater than 0. Used only as a fallback — see note below
  • bookingTotal (number, optional): The full booking total. When provided, the server recomputes the chargeable amount from this (full vs. deposit) rather than trusting amount
  • paymentOption (string, optional): "full" or "deposit". Only honored when the hotel both takes deposits and allows the guest to choose; otherwise the server decides
  • currency (string, optional): ISO currency code. Defaults to the property's preferred/default currency
  • metadata (object, optional): Extra metadata to attach to the PaymentIntent (e.g. { "reservationId": "..." })

Example Request

bash
curl -X POST https://app.hotelbee.co/api/booking-engine/create-payment-intent \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 150.00,
    "bookingTotal": 500.00,
    "paymentOption": "deposit",
    "currency": "eur",
    "metadata": { "reservationId": "reservations_2026-03-24T14:30:00Z" }
  }'

Response 200 — Success

json
{
  "clientSecret": "pi_3Nabc..._secret_xyz",
  "paymentIntentId": "pi_3Nabc..."
}

Response 400 — Errors

json
{
  "error": "Online payments are not enabled for this property"
}

Other possible 400 errors:

  • "Valid amount is required"amount (or the server-computed chargeable amount) is missing or ≤ 0
  • "Property Missing" — the Property header was not sent

Important Notes

  • Server is authoritative for the amount: When bookingTotal is provided, the charge is computed server-side from the hotel's deposit settings. The client amount is only used as a fallback when no bookingTotal is given. Don't rely on amount to set a partial charge — pass bookingTotal + paymentOption instead
  • Deposits: If the hotel charges a deposit (deposit percentage between 1–99) the intent is for the deposit amount unless the hotel allows full payment and the guest chose "full"
  • Requires Stripe Connect: The property must have an active Stripe Connect account with booking-engine payments enabled, or the call returns 400

Create Reservation

Creates a new reservation in HotelBee. This is the core endpoint for booking rooms through the booking engine.

POST /reservations — Creates a new reservation with room assignment and optional payment. Auth required (API key). Content-Type: application/json.

Request Parameters

  • checkIn (string, required): Check-in date in YYYY-MM-DD format
  • checkOut (string, required): Check-out date in YYYY-MM-DD format
  • roomTypes (array, required): Array of room type bookings (at least one required)
  • clientDetails (object, required): Guest contact information
  • specialRequests (string, optional): Special requests or notes
  • promoCode (string, optional): Promo code to apply. Re-validated server-side against the property's promo codes. This is the way to apply a discount
  • selectedPaymentMethod (object, optional): Payment method selection
  • stripePaymentIntentId (string, optional): A confirmed Stripe PaymentIntent ID — see Payment Paths
  • externalPayment (object, optional): A charge processed on your own gateway — see Payment Paths

Payment Paths

A reservation is created with exactly one payment path, or none ("pay at hotel"). The paths are mutually exclusive — sending more than one returns 400 conflicting_payment_methods.

| Path | Field | Notes | |---|---|---| | Stripe (HotelBee Payments) | stripePaymentIntentId | Property must have Stripe enabled. The payment is verified before the reservation is confirmed | | Your own gateway | externalPayment object | Must be enabled for your key — contact support | | None (pay at hotel) | — | |

externalPayment Object

Reports a charge you took on your own gateway. Use the custom-gateway flow: POST /quote → charge on your gateway → create the reservation with externalPayment.

  • amount (number, required): The amount you charged. Must be > 0
  • transactionId (string, required\): Your gateway's charge/transaction ID. \Either transactionId or reference is required
  • reference (string, required\*): Alternative to transactionId
  • gateway (string, optional): Label for your gateway (e.g. "stripe-acme")
  • currency (string, optional): Currency of the charge
  • status (string, optional): Defaults to "success". Must be one of success, succeeded, paid, completed, captured, approved — otherwise 400 payment_not_completed
  • cardBrand (string, optional): Display only (e.g. "visa")
  • cardLast4 (string, optional): Display only
  • note (string, optional): Free-text note

roomTypes[] Object

  • roomTypeId (string, required): Room type ID (from /roomtypes endpoint)
  • rateId (string, optional): Rate ID (from /roomtypes rates array)
  • adults (integer, required): Number of adults
  • children (integer, optional): Number of children
  • quantity (integer, required): Number of rooms of this type
  • services (array, optional): Selected add-on services ([{ serviceId }])

clientDetails Object

  • firstName (string, required): Guest first name
  • lastName (string, required): Guest last name
  • email (string, required): Guest email address
  • phone (string, optional): Phone number
  • city (string, optional): City
  • country (string, optional): Country code (e.g., "AL", "US")
  • addressLine (string, optional): Street address
  • zip (string, optional): Postal code
  • state (string, optional): State/province
  • company (string, optional): Company name

Example Request

bash
curl -X POST https://app.hotelbee.co/api/booking-engine/reservations \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "Content-Type: application/json" \
  -d '{
    "checkIn": "2026-04-01",
    "checkOut": "2026-04-05",
    "roomTypes": [
      {
        "roomTypeId": "roomtype_2026-01-15T10:00:00Z",
        "rateId": "rate_spring_001",
        "adults": 2,
        "children": 1,
        "quantity": 1,
        "services": [
          { "serviceId": "service_spa_001" }
        ]
      }
    ],
    "clientDetails": {
      "firstName": "John",
      "lastName": "Doe",
      "email": "john@example.com",
      "phone": "+355 69 123 4567",
      "country": "US"
    },
    "specialRequests": "Non-smoking room preferred",
    "selectedPaymentMethod": {
      "id": "method_bank_001",
      "name": "Bank Transfer",
      "method": "bank"
    }
  }'

Response 200 — Success

json
{
  "reservationId": "reservations_2026-03-24T14:30:00Z",
  "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "PENDING"
}

Response 400 — Validation Error

json
{
  "error": "checkIn, checkOut, and roomTypes are required"
}

Other possible errors:

  • "room_not_available" (400) — Requested rooms cannot be assigned (no availability)
  • "invalid_promo_code" (400) — The promoCode could not be validated
  • "raw_card_not_supported" (400) — Raw card details were sent; use Stripe or your own gateway instead
  • "conflicting_payment_methods" (400) — More than one payment path was sent
  • "external_payment_amount_invalid" (400) — externalPayment.amount missing or ≤ 0
  • "external_payment_transaction_id_required" (400) — Neither transactionId nor reference provided
  • "payment_not_completed" (400) — externalPayment.status is not a completed status
  • "payment_verification_failed" (400) — A stripePaymentIntentId could not be verified as paid
  • "external_payments_not_enabled" (403) — Your key sent externalPayment but your own gateway isn't enabled for it
  • "Forbidden - API key is not authorized for this property" (403) — The Property is not one your key is allowed to use

Reservation Processing Flow

When a reservation is successfully submitted:

  1. Input Validation: checkIn, checkOut, and roomTypes are validated
  2. Availability Check: Availability for the date range is loaded and rooms are verified
  3. Client Resolution: An existing client is matched by email, or a new one is created
  4. Room Assignment: Specific rooms are assigned from the available pool
  5. Price Calculation: Daily prices are calculated, including extra-guest charges
  6. Service Processing: Selected services are added with tax calculations
  7. Discount Application: A promoCode, if provided, is validated and applied
  8. Payment: The supplied payment (Stripe or your own gateway) is processed
  9. Reservation Created: The reservation is created with status PENDING and a generated UUID

Important Notes

  • UUID: The uuid in the response is used for guest portal login
  • Client Deduplication: If a client with the same email exists, the existing record is used
  • Availability: Availability is re-checked before the reservation is confirmed, so overbooking is rejected

Custom Gateway (Bring Your Own Payment Provider)

For integrators who process payments on their own payment gateway instead of HotelBee Payments (Stripe). This must be enabled for your key — contact support.

The flow is three steps, and the key is to use /quote so your charge amount can never drift from what HotelBee prices the reservation at:

  1. Get the authoritative amountPOST /quote with the selection (checkIn, checkOut, roomTypes[], optional promoCode). No client, reservation, or payment is created.
  2. Charge the guest on your own gateway for the returned amount in currency (the description is suitable as the charge descriptor).
  3. Create the reservation, reporting the chargePOST /reservations with the same selection plus an externalPayment object carrying the amount and your gateway's transactionId (or reference).
bash
# Step 3 — create the reservation with the reported charge
curl -X POST https://app.hotelbee.co/api/booking-engine/reservations \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: acmetravel" \
  -H "Content-Type: application/json" \
  -d '{
    "checkIn": "2026-07-01",
    "checkOut": "2026-07-04",
    "roomTypes": [
      { "roomTypeId": "roomtype_2026-01-15T10:00:00Z", "rateId": "rate_spring_001", "adults": 2, "quantity": 1 }
    ],
    "clientDetails": { "firstName": "Jane", "lastName": "Doe", "email": "jane@example.com" },
    "externalPayment": {
      "amount": 270.00,
      "transactionId": "ch_abc123",
      "gateway": "stripe-acme",
      "currency": "EUR",
      "status": "success",
      "cardBrand": "visa",
      "cardLast4": "4242",
      "note": "Charged via Acme checkout"
    }
  }'

See externalPayment Object for full field and error details.

Validate Promo Code

Validates a promotional code and returns the discount details if valid.

POST /promocode/validate — Validates a promo code and returns discount info. Auth required (API key). Content-Type: application/json.

Request Parameters

  • code (string, required): The promotional code to validate

Example Request

bash
curl -X POST https://app.hotelbee.co/api/booking-engine/promocode/validate \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "Content-Type: application/json" \
  -d '{"code": "SUMMER10"}'

Response 200 — Valid Code

json
{
  "valid": true,
  "discount": {
    "type": "percentage",
    "value": 10
  }
}

Response 200 — Invalid Code

json
{
  "valid": false
}

Discount Types

  • percentage: Percentage discount (value between 0-100)
  • total: Fixed amount discount (subtracted from total)

Important Notes

  • Apply a discount by passing the code. Pass the validated code as promoCode on POST /reservations (and /quote); the server re-validates and applies it. Use /promocode/validate only to give the guest immediate feedback before they book
  • Discount type normalization: internal types "fixed" and "total" are both returned as "total"
  • Percentage values are clamped between 0 and 100

Search Clients

Searches for existing clients by email prefix. Useful for auto-filling guest details during booking.

GET /clients/search — Searches clients by email prefix. Auth required (API key).

Not available to integration keys. This endpoint exposes guest personal
data and is restricted; integration keys receive 403 Forbidden.

Query Parameters

  • q (string, required): Email search prefix (minimum 2 characters)

Example Request

bash
curl -X GET "https://app.hotelbee.co/api/booking-engine/clients/search?q=john@" \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine"

Response 200 — Success

json
{
  "clients": [
    {
      "id": "client_001",
      "firstName": "John",
      "lastName": "Doe",
      "email": "john@example.com",
      "phone": "+355 69 123 4567",
      "city": "New York",
      "country": "US",
      "addressLine": "123 Main St",
      "zip": "10001",
      "state": "NY",
      "company": "Acme Corp"
    }
  ]
}

Use Cases

  • Auto-complete guest details during the booking flow
  • Pre-fill returning guest information

Get OTA Prices

Retrieves pricing from OTA channels (e.g., Booking.com, Expedia) via Channex integration. Use this to display price comparisons.

GET /ota-prices — Fetches OTA channel prices for comparison. Auth required (API key).

Query Parameters

  • startDate (string, required): Start date in YYYY-MM-DD format
  • endDate (string, required): End date in YYYY-MM-DD format
  • roomTypeId (string, optional): Filter to a specific room type

Example Request

bash
curl -X GET "https://app.hotelbee.co/api/booking-engine/ota-prices?startDate=2026-04-01&endDate=2026-04-05" \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine"

Response 200 — Success (Channex configured)

json
{
  "available": true,
  "channels": [
    {
      "channelId": "ch_001",
      "channelName": "Booking.com",
      "channel": "booking_com",
      "channelGroup": "otas",
      "roomTypes": [
        {
          "roomTypeId": "roomtype_2026-01-15T10:00:00Z",
          "roomTypeName": "Deluxe Room",
          "ratePlanName": "Standard Rate",
          "currency": "EUR",
          "rates": {
            "2026-04-01": 120.50,
            "2026-04-02": 120.50,
            "2026-04-03": 125.00,
            "2026-04-04": 125.00
          },
          "totalPrice": 491.00,
          "nightlyAverage": 122.75,
          "nights": 4
        }
      ]
    }
  ]
}

Response 200 — Channex not configured

json
{
  "available": false,
  "channels": []
}

Important Notes

  • Requires Channex: This endpoint only works if the property has an active Channex integration
  • Caching: Channel data is cached for 1 hour, rate plans for 1 hour, restrictions for 15 minutes
  • Price Multipliers: OTA prices include channel-specific price adjustments (increase/decrease by % or fixed amount)
  • Room Type Mapping: Internal room types are matched to Channex room types via channexId

Guest Portal Endpoints

The Guest Portal provides a self-service interface for guests to view their reservations, invoices, orders, and perform online check-in.

Authentication: Guest portal endpoints that require auth need both the API key headers AND a guest JWT token obtained from /guest-portal/login.

Public endpoints (no guest token required): /guest-portal/information, /guest-portal/things-to-do, /guest-portal/services, /guest-portal/pospoints

Guest Portal Login

Authenticates a guest using their reservation UUID and last name. Returns a JWT token for subsequent guest portal requests.

POST /guest-portal/login — Authenticates a guest and returns a JWT token. Auth required (API key only — no guest token needed). Content-Type: application/json.

Request Parameters

  • uuid (string, required): The reservation number / UUID (from the reservation confirmation). The field is named uuid, and the value is the reservation's UUID
  • lastName (string, required): Primary guest's last name. The match is case-insensitive and trimmed of surrounding whitespace

Example Request

bash
curl -X POST https://app.hotelbee.co/api/booking-engine/guest-portal/login \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "Content-Type: application/json" \
  -d '{"uuid": "a1b2c3d4-e5f6-7890", "lastName": "Doe"}'

Response 200 — Success

json
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "client": {
    "_id": "client_001",
    "firstName": "John",
    "lastName": "Doe",
    "email": "john@example.com",
    "phone": "+355 69 123 4567"
  }
}

Response 400 — Missing Parameters

json
{
  "error": "uuid and lastName are required"
}

Response 401 — Invalid Credentials

json
{
  "error": "Invalid reservation number or last name"
}
The same "Invalid reservation number or last name" message is returned
whether the reservation number is unknown or the last name doesn't match — the
endpoint does not distinguish, by design.

Important Notes

  • Token Expiry: The JWT token is valid for 7 days
  • Token Usage: Include the token as the X-Guest-Token header in subsequent guest portal requests. Do not use Authorization for the guest token — that header carries the API key (see Guest Portal Authentication)
  • UUID Source: The UUID is returned in the POST /reservations response and typically included in the confirmation email

Get Guest Profile

Retrieves the authenticated guest's profile information.

GET /guest-portal/me — Returns the authenticated guest's client profile. Auth required (API key + guest token).

Example Request

bash
curl -X GET https://app.hotelbee.co/api/booking-engine/guest-portal/me \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "X-Guest-Token: eyJhbGci..."

Response 200 — Success

json
{
  "_id": "client_001",
  "firstName": "John",
  "lastName": "Doe",
  "email": "john@example.com",
  "phone": "+355 69 123 4567",
  "referenceTitle": "Mr.",
  "birthdate": "1980-01-15",
  "nationality": "US",
  "company": "Acme Corp",
  "addressLine": "123 Main St",
  "city": "New York",
  "zip": "10001",
  "state": "NY",
  "country": "US",
  "IDType": "Passport",
  "IDNumber": "A12345678",
  "dateOfIssue": "2015-01-01",
  "dateOfExpiry": "2025-01-01"
}

Get Guest Reservations

Retrieves all reservations linked to the authenticated guest, sorted by check-in date.

GET /guest-portal/reservations — Returns all reservations for the authenticated guest. Auth required (API key + guest token).

Example Request

bash
curl -X GET https://app.hotelbee.co/api/booking-engine/guest-portal/reservations \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "X-Guest-Token: eyJhbGci..."

Response 200 — Success

json
[
  {
    "_id": "reservations_2026-03-24T14:30:00Z",
    "uuid": "a1b2c3d4-e5f6-7890",
    "checkin": "2026-04-01",
    "checkout": "2026-04-05",
    "status": "APPROVED",
    "totalPrice": 500.00,
    "invoiceCurrency": "EUR",
    "rooms": [
      {
        "roomId": "room_101",
        "name": "Room 101"
      }
    ],
    "clientsData": [
      {
        "firstName": "John",
        "lastName": "Doe",
        "email": "john@example.com"
      }
    ]
  }
]

Important Notes

  • clientsData: Only included for APPROVED reservations with future check-in dates (for online check-in eligibility)
  • Sorting: Results are sorted by check-in date
  • Room Names: Room IDs are resolved to human-readable room names

Get Guest Invoices

Retrieves all invoices linked to the authenticated guest.

GET /guest-portal/invoices — Returns all invoices for the authenticated guest. Auth required (API key + guest token).

Response 200 — Success

json
[
  {
    "_id": "invoice_001",
    "number": "INV-001",
    "serial": "2026",
    "clientName": "John Doe",
    "date": "2026-04-01",
    "status": "paid",
    "totalPrice": 500.00,
    "invoiceCurrency": "EUR",
    "tableData": [
      {
        "itemName": "Room 101 - Deluxe Room",
        "description": "4 nights (Apr 1 - Apr 5)",
        "quantity": 1,
        "unit": "night",
        "price": 450.00
      },
      {
        "itemName": "Spa Access",
        "description": "4 nights",
        "quantity": 4,
        "unit": "night",
        "price": 25.00
      }
    ]
  }
]

Get Guest Orders

Retrieves all POS orders associated with the guest's reservations.

GET /guest-portal/orders — Returns POS orders for all guest reservations. Auth required (API key + guest token).

Response 200 — Success

json
[
  {
    "_id": "order_001",
    "totalPrice": 75.50,
    "subtotal": 70.00,
    "status": "completed",
    "createdAt": "2026-04-02T10:30:00Z",
    "reservationId": "reservations_2026-03-24T14:30:00Z",
    "pospointId": "pospoint_001",
    "currency": "EUR",
    "customerName": "Restaurant",
    "products": [
      {
        "productId": "product_001",
        "name": "Coffee",
        "quantity": 2,
        "price": 5.00,
        "note": "Extra hot"
      },
      {
        "productId": "product_002",
        "name": "Club Sandwich",
        "quantity": 1,
        "price": 15.00,
        "note": ""
      }
    ]
  }
]

Important Notes

  • Orders are fetched across all reservations linked to the guest
  • Product names and POS point currencies are resolved via batch lookups

Online Check-in

Performs online check-in for an eligible reservation. Updates guest details and changes reservation status.

POST /guest-portal/checkin — Performs online check-in and updates guest information. Auth required (API key + guest token). Content-Type: application/json.

Request Parameters

  • reservationId (string, required): Reservation ID to check in
  • clientsData (object, required): Map of client ID → updated guest details

clientsData[clientId] Object

  • referenceTitle (string, optional): Title (Mr., Mrs., Ms., etc.)
  • firstName (string, optional): First name
  • lastName (string, optional): Last name
  • country (string, optional): Country code
  • email (string, optional): Email address
  • phone (string, optional): Phone number
  • IDType (string, optional): ID document type (Passport, ID Card, etc.)
  • IDNumber (string, optional): ID document number
  • birthdate (string, optional): Date of birth (YYYY-MM-DD)
  • nationality (string, optional): Nationality
  • dateOfIssue (string, optional): ID issue date (YYYY-MM-DD)
  • dateOfExpiry (string, optional): ID expiry date (YYYY-MM-DD)

Example Request

bash
curl -X POST https://app.hotelbee.co/api/booking-engine/guest-portal/checkin \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "X-Guest-Token: eyJhbGci..." \
  -H "Content-Type: application/json" \
  -d '{
    "reservationId": "reservations_2026-03-24T14:30:00Z",
    "clientsData": {
      "client_001": {
        "referenceTitle": "Mr.",
        "firstName": "John",
        "lastName": "Doe",
        "country": "US",
        "IDType": "Passport",
        "IDNumber": "A12345678",
        "birthdate": "1980-01-15",
        "nationality": "US"
      }
    }
  }'

Response 200 — Success

json
{
  "success": true,
  "reservation": {
    "_id": "reservations_2026-03-24T14:30:00Z",
    "status": "CHECKIN",
    "checkin": "2026-04-01",
    "checkout": "2026-04-05"
  }
}

Response 400 / 404 — Error

json
{
  "error": "Reservation not eligible for check-in"
}

Eligibility Rules

  • Reservation must have status APPROVED
  • Check-in date must be today or in the future

Processing Details

  1. Validates reservation eligibility
  2. Updates the guest details with the provided fields
  3. Changes the reservation status to CHECKIN
  4. Notifies the hotel and updates connected systems

Get Reservation Policy

Returns the cancellation/modification policy evaluation for one of the guest's reservations — used to show whether a cancellation or change incurs a penalty before the guest commits.

GET /guest-portal/reservations/:reservationId/policy — Returns the resolved policy and penalty evaluation for a reservation. Auth required (API key + guest token).

Path Parameters

  • reservationId (string, required): ID of one of the authenticated guest's reservations

Example Request

bash
curl -X GET https://app.hotelbee.co/api/booking-engine/guest-portal/reservations/reservations_2026-03-24T14:30:00Z/policy \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "X-Guest-Token: eyJhbGci..."

The response is the evaluated policy object (fields depend on the policy configured for the reservation). A penalty value of null means no penalty currently applies; a non-null value indicates the penalty that would be charged. Returns 404 if the reservation is not linked to the authenticated guest.

Cancel Reservation

Cancels one of the authenticated guest's reservations, applying the cancellation policy and notifying the hotel and connected channels.

POST /guest-portal/reservations/:reservationId/cancel — Cancels a reservation. Auth required (API key + guest token).

Path Parameters

  • reservationId (string, required): ID of the reservation to cancel

Example Request

bash
curl -X POST https://app.hotelbee.co/api/booking-engine/guest-portal/reservations/reservations_2026-03-24T14:30:00Z/cancel \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "X-Guest-Token: eyJhbGci..."

Response 200 — Success

json
{
  "success": true,
  "reservation": {
    "_id": "reservations_2026-03-24T14:30:00Z",
    "uuid": "a1b2c3d4-e5f6-7890",
    "status": "CANCELLED"
  },
  "penaltyApplied": null
}

Response 400 — Not Cancellable

json
{
  "error": "Reservation cannot be cancelled",
  "reason": "Current status \"CHECKIN\" does not allow cancellation"
}

Important Notes

  • Eligible statuses: Only APPROVED and DRAFT reservations can be cancelled
  • penaltyApplied: Reflects the cancellation policy. When a penalty applies, the related invoice is kept (status not cancelled); when there is no penalty, reservation invoices are cancelled
  • Side effects: The cancellation is synced to connected sales channels, and the guest and hotel are emailed

Change Reservation Dates

Moves an existing reservation to new check-in/check-out dates after verifying availability and recalculating the price. A preview variant computes the new total without saving.

POST /guest-portal/reservations/:reservationId/change-dates — Applies new dates. Auth required (API key + guest token). Content-Type: application/json.

POST /guest-portal/reservations/:reservationId/change-dates/preview — Dry run: checks availability and returns the new total without saving. Same auth.

Request Parameters

  • newCheckin (string, required): New check-in date (YYYY-MM-DD)
  • newCheckout (string, required): New check-out date (YYYY-MM-DD). Must be after newCheckin

Example Request (preview)

bash
curl -X POST https://app.hotelbee.co/api/booking-engine/guest-portal/reservations/reservations_2026-03-24T14:30:00Z/change-dates/preview \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "X-Guest-Token: eyJhbGci..." \
  -H "Content-Type: application/json" \
  -d '{ "newCheckin": "2026-04-10", "newCheckout": "2026-04-14" }'

Response 200 — Preview (available)

json
{
  "available": true,
  "currentTotal": 500.00,
  "newTotal": 540.00,
  "currency": "EUR"
}

Response 200 — Preview (not available)

json
{
  "available": false,
  "reason": "room_not_available",
  "message": "Room type is not available for the selected dates"
}

Response 200 — Apply (success)

json
{
  "success": true,
  "reservation": {
    "_id": "reservations_2026-03-24T14:30:00Z",
    "uuid": "a1b2c3d4-e5f6-7890",
    "checkin": "2026-04-10",
    "checkout": "2026-04-14",
    "status": "APPROVED",
    "totalPrice": 540.00,
    "invoiceCurrency": "EUR",
    "rooms": [{ "roomId": "room_101" }]
  },
  "penaltyApplied": null
}

Important Notes

  • Eligible status: Only APPROVED reservations can have their dates changed
  • Availability: The apply call re-checks availability and returns 400 with reason room_not_available or rate_not_available if the new dates can't be honored
  • Side effects: The hotel is emailed a modification notice

Change Room Guests

Changes the guest mix (per category) for one room in a reservation, re-pricing extra-guest charges. Includes a capacity helper and a preview variant.

GET /guest-portal/reservations/:reservationId/room-capacity — Returns the max capacity for a room and the primary guest category name. Auth required (API key + guest token).

POST /guest-portal/reservations/:reservationId/change-guests/preview — Dry run: returns the new total without saving. Same auth. Content-Type: application/json.

POST /guest-portal/reservations/:reservationId/change-guests — Applies the new guest mix. Same auth. Content-Type: application/json.

room-capacity — Query Parameters

  • roomId (string, required): Room within the reservation to inspect

room-capacity — Response 200

json
{
  "maxCapacity": 4,
  "primaryCategoryName": "Adults"
}

change-guests — Request Parameters

  • roomId (string, required): Room within the reservation to update
  • guests (array, required): New guest mix, e.g. [{ "name": "Adults", "number": 2 }, { "name": "Children", "number": 1 }]. Total must be at least 1 and not exceed the room's maxCapacity

Example Request (apply)

bash
curl -X POST https://app.hotelbee.co/api/booking-engine/guest-portal/reservations/reservations_2026-03-24T14:30:00Z/change-guests \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "X-Guest-Token: eyJhbGci..." \
  -H "Content-Type: application/json" \
  -d '{
    "roomId": "room_101",
    "guests": [
      { "name": "Adults", "number": 2 },
      { "name": "Children", "number": 1 }
    ]
  }'

change-guests/preview — Response 200

json
{
  "currentTotal": 500.00,
  "newTotal": 525.00,
  "currency": "EUR"
}

change-guests — Response 200 (apply success)

json
{
  "success": true,
  "reservation": {
    "_id": "reservations_2026-03-24T14:30:00Z",
    "uuid": "a1b2c3d4-e5f6-7890",
    "checkin": "2026-04-01",
    "checkout": "2026-04-05",
    "status": "APPROVED",
    "totalPrice": 525.00,
    "invoiceCurrency": "EUR",
    "guests": []
  }
}

Response 400 — Exceeds Capacity

json
{
  "error": "exceeds_capacity",
  "message": "Total guests (5) exceeds room maximum capacity (4)",
  "maxCapacity": 4
}

Important Notes

  • Eligible statuses: APPROVED and CHECKIN reservations can have guests changed
  • Pricing: Extra-guest charges are recomputed from the room type's capacity tiers and included-capacity for the stay

Order a Service

Adds a bookable service (e.g. spa, airport transfer) to a room on one of the guest's reservations and notifies the hotel.

POST /guest-portal/services/order — Orders a service for a room. Auth required (API key + guest token). Content-Type: application/json.

Request Parameters

  • reservationId (string, required): Reservation to add the service to
  • roomId (string, required): Room within the reservation
  • serviceId (string, required): ID of a booking-engine-enabled service (from /guest-portal/services)
  • note (string, optional): Free-text note for the order

Example Request

bash
curl -X POST https://app.hotelbee.co/api/booking-engine/guest-portal/services/order \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "X-Guest-Token: eyJhbGci..." \
  -H "Content-Type: application/json" \
  -d '{
    "reservationId": "reservations_2026-03-24T14:30:00Z",
    "roomId": "room_101",
    "serviceId": "service_spa_001",
    "note": "Around 3pm please"
  }'

Response 200 — Success

json
{
  "success": true,
  "reservation": {
    "_id": "reservations_2026-03-24T14:30:00Z",
    "uuid": "a1b2c3d4-e5f6-7890",
    "totalPrice": 525.00,
    "status": "APPROVED"
  }
}

Important Notes

  • Eligible statuses: APPROVED and CHECKIN
  • Service must be enabled: Only services with bookingEngineEnabled: true can be ordered; the booking-engine price is used when set
  • The service is added to the room, the reservation total is recalculated, and the hotel receives a notification email

Sign an Invoice

Stores the guest's signature on one of their invoices (used for check-in paperwork and registration forms).

POST /guest-portal/invoice/:invoiceId/sign — Saves a signature on an invoice. Auth required (API key + guest token). Content-Type: application/json.

Path Parameters

  • invoiceId (string, required): Invoice to sign (must belong to one of the guest's reservations)

Request Parameters

Provide a signature as either:

  • url (string): URL of an already-uploaded signature image, or
  • imageData (string): Base64/binary image data to upload server-side

Example Request

bash
curl -X POST https://app.hotelbee.co/api/booking-engine/guest-portal/invoice/invoice_001/sign \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "X-Guest-Token: eyJhbGci..." \
  -H "Content-Type: application/json" \
  -d '{ "imageData": "data:image/png;base64,iVBORw0KGgo..." }'

Response 200 — Success

json
{
  "success": true,
  "signature": {
    "url": "https://storage.blob.core.windows.net/hotel/signatures/invoices/sig_001.png",
    "signedBy": "client_001",
    "source": "guest_portal"
  }
}

Important Notes

  • Authorization: The signature is rejected with 403 unless the guest's client ID is the invoice customer or appears on one of the invoice's reservations
  • One signature per invoice: Signing replaces any previous signature on the invoice
  • Returns 404 if the invoice does not exist, 400 if neither url nor imageData is provided

Guest Messaging

A lightweight two-way messaging thread between the guest and hotel staff. All four endpoints require the API key headers + guest token.

GET /guest-portal/messages/conversations — Lists the guest's conversations. Query: limit (default 20, max 50), before (cursor for pagination).

GET /guest-portal/messages/:conversationId — Lists messages in a conversation. Query: limit (default 20, max 50), before.

POST /guest-portal/messages — Sends a message from the guest. Content-Type: application/json.

POST /guest-portal/messages/read — Marks messages as read by the guest. Content-Type: application/json.

Send a Message — Request Parameters

  • body (string, required): Message text (trimmed; max 2000 characters)
  • conversationId (string, optional): Existing conversation to append to; omit to start a new one
  • reservationId (string, optional): Reservation the message relates to
  • roomId (string, optional): Room the message relates to

Mark Read — Request Parameters

  • ids (array, optional): Message IDs to mark read for the guest

Example Request (send)

bash
curl -X POST https://app.hotelbee.co/api/booking-engine/guest-portal/messages \
  -H "Authorization: Bearer your-api-key" \
  -H "Property: myhotel" \
  -H "Username: booking_engine" \
  -H "X-Guest-Token: eyJhbGci..." \
  -H "Content-Type: application/json" \
  -d '{ "body": "What time is breakfast?", "reservationId": "reservations_2026-03-24T14:30:00Z" }'

Response 200 — Send

json
{
  "message": {
    "_id": "guestmessages_001",
    "conversationId": "conversation_001",
    "senderType": "guest",
    "body": "What time is breakfast?",
    "createdAt": "2026-04-01T09:00:00Z"
  }
}

Response shapes

  • GET .../conversations{ "conversations": [ ... ] }
  • GET .../:conversationId{ "messages": [ ... ] }
  • POST .../messages{ "message": { ... } }
  • POST .../messages/read{ "updatedIds": [ ... ] }
Sending a guest message notifies the hotel so staff can reply.

Get Property Information (Public)

Retrieves general property information for the guest portal. No guest token required.

GET /guest-portal/information — Returns guest portal information settings. Auth required (API key only — no guest token needed).

Response 200 — Success

json
{
  "locationLink": "https://maps.google.com/?q=...",
  "wifiName": "HotelRiviera_WiFi",
  "wifiPassword": "welcome2026",
  "goodToKnow": "Breakfast is served 7-10am in the main restaurant"
}

Get Things To Do (Public)

Retrieves activities and attractions for guests. No guest token required.

GET /guest-portal/things-to-do — Returns activities and attractions. Auth required (API key only — no guest token needed).

Response 200 — Success

json
[
  {
    "_id": "thing_001",
    "title": "City Walking Tour",
    "description": "A guided tour of the old city center",
    "location": "Old Town Square",
    "category": "tours",
    "images": ["https://storage.blob.core.windows.net/hotel/tour1.jpg"]
  }
]

Get Services (Public)

Retrieves hotel services available for booking. No guest token required.

GET /guest-portal/services — Returns booking-engine-enabled services. Auth required (API key only — no guest token needed).

Response 200 — Success

json
[
  {
    "_id": "service_spa_001",
    "name": "Spa Access",
    "description": "Full access to spa and wellness facilities",
    "price": 50.00,
    "calculationType": "per_night",
    "frequency": "nightly",
    "images": ["https://storage.blob.core.windows.net/hotel/spa.jpg"]
  }
]

Important Notes

  • Only services with bookingEngineEnabled: true are returned
  • Uses bookingEnginePrice if set, otherwise falls back to price
  • Uses bookingEngineImages if set, otherwise uses default images

Get POS Points (Public)

Retrieves POS points that have online menu enabled. No guest token required.

GET /guest-portal/pospoints — Returns POS points with online menu enabled. Auth required (API key only — no guest token needed).

Response 200 — Success

json
[
  {
    "_id": "pospoint_001",
    "name": "Restaurant"
  },
  {
    "_id": "pospoint_002",
    "name": "Pool Bar"
  }
]

Important Notes

  • Only POS points with hasOnlineMenu: true are returned

Proposals

Proposals are quotes the hotel sends to a prospective guest as a public shareable link. The recipient opens the link to view the quote, then accepts (choosing an option) or rejects it.

Different base path and auth model. Proposal endpoints live under
https://app.hotelbee.co/api/proposals, not under /api/booking-engine.
They use no headers — authentication is the unguessable token embedded
in the URL. There is no API key and no guest JWT. The property slug is also a
path segment, not the Property header.

Base path:

https://app.hotelbee.co/api/proposals/{property}/{token}

Get Proposal

GET /api/proposals/:property/:token — Returns the proposal, the client it was sent to, property branding, and the room types / products / services / taxes referenced by its options. No auth headers.

Path Parameters

  • property (string, required): Your property name
  • token (string, required): The proposal's public token (from the share link)

Example Request

bash
curl -X GET https://app.hotelbee.co/api/proposals/myhotel/9f3c1a7b8e2d4f60 

Response 200 — Success (abridged)

json
{
  "proposal": {
    "_id": "proposal_001",
    "number": "Q-2026-014",
    "status": "SENT",
    "checkIn": "2026-07-01",
    "checkOut": "2026-07-05",
    "guests": null,
    "introText": "Thank you for your interest...",
    "closingText": "We look forward to hosting you.",
    "options": [],
    "selectedOptionId": null,
    "acceptance": null,
    "invoiceCurrency": "EUR",
    "pdfLanguage": "en",
    "date": "2026-06-01",
    "dueDate": "2026-06-15",
    "publicToken": "9f3c1a7b8e2d4f60"
  },
  "client": {
    "_id": "client_001",
    "firstName": "John",
    "lastName": "Doe",
    "company": "Acme Corp",
    "email": "john@example.com",
    "phone": "+355 69 123 4567"
  },
  "property": {
    "propertyId": "myhotel",
    "name": "Hotel Riviera",
    "logo": "https://storage.blob.core.windows.net/hotel/logo.png",
    "proposalThemeColor": "#1a237e",
    "proposalCoverImage": "https://storage.blob.core.windows.net/hotel/cover.jpg"
  },
  "roomtypes": [],
  "products": [],
  "services": [],
  "taxes": []
}

Returns 404 ("Proposal not found") if the token does not resolve to a proposal.

Accept Proposal

POST /api/proposals/:property/:token/accept — Accepts the proposal, recording the chosen option and the guest's IP/User-Agent for the acceptance record. Content-Type: application/json.

Request Parameters

  • optionId (string, required): ID of the option the guest is accepting
  • guestComment (string, optional): Optional note from the guest

Response 200 — Success

json
{
  "success": true,
  "proposal": {
    "_id": "proposal_001",
    "status": "ACCEPTED",
    "selectedOptionId": "option_002",
    "acceptance": { "acceptedAt": "2026-06-05T12:00:00Z" }
  }
}

Errors

  • 400"Missing optionId"
  • 404"Proposal not found" or "Invalid option"
  • 409 — proposal was already accepted/rejected

Reject Proposal

POST /api/proposals/:property/:token/reject — Rejects the proposal. Content-Type: application/json.

Request Parameters

  • guestComment (string, optional): Optional reason from the guest

Response 200 — Success

json
{
  "success": true,
  "proposal": {
    "_id": "proposal_001",
    "status": "REJECTED",
    "acceptance": null
  }
}

Errors

  • 404"Proposal not found"
  • 409 — proposal was already accepted/rejected

Integration Workflow

Booking Engine Integration Steps

  1. Get Credentials: Contact support@hotelbee.co with your property name
  2. Fetch Config: Get branding and settings (GET /config)
  3. Fetch Room Types: Retrieve room catalog with amenities and rates (GET /roomtypes)
  4. Fetch Currencies: Get supported currencies (GET /currencies)
  5. Check Availability: Query date-range availability (GET /availabilities)
  6. Build Booking UI: Create your booking flow with room selection, date picker, and guest form
  7. Validate Promo Code (optional): Check promo codes (POST /promocode/validate)
  8. Search Clients (optional, if enabled for your key): Auto-fill returning guest details (GET /clients/search)
  9. Compare OTA Prices (optional): Show price comparisons (GET /ota-prices)
  10. Take Payment (choose one): collect a Stripe payment (POST /create-payment-intent), or — for your own gateway — quote then charge (POST /quote, then your gateway). See Payment Paths
  11. Create Reservation: Submit the booking (POST /reservations), including the chosen payment field (stripePaymentIntentId, externalPayment, cardDetails, or none)

Guest Portal Integration Steps

  1. Guest Login: Authenticate with UUID + last name (POST /guest-portal/login)
  2. Load Profile: Show guest details (GET /guest-portal/me)
  3. Show Reservations: Display booking history (GET /guest-portal/reservations)
  4. Show Invoices: Display billing (GET /guest-portal/invoices)
  5. Show Orders: Display POS order history (GET /guest-portal/orders)
  6. Online Check-in: Allow self-service check-in (POST /guest-portal/checkin)
  7. Manage the Booking (optional): Let guests review policy and cancel or modify (GET .../reservations/:id/policy, POST .../cancel, POST .../change-dates, POST .../change-guests)
  8. Order Services / Sign Invoices (optional): Add services or capture signatures (POST /guest-portal/services/order, POST /guest-portal/invoice/:invoiceId/sign)
  9. Message the Hotel (optional): Two-way messaging (GET/POST /guest-portal/messages...)
  10. Property Info: Show hotel info, WiFi, activities (GET /guest-portal/information, /things-to-do, /services)
  • Config & Room Types: Cache for 1-4 hours (change infrequently)
  • Currencies: Cache for 24 hours
  • Availability: Always fetch fresh (real-time inventory)
  • OTA Prices: API caches internally (channels: 1hr, rates: 1hr, restrictions: 15min)

Integration Example

JavaScript/Node.js:

js
const API_BASE = 'https://app.hotelbee.co/api/booking-engine';
const headers = {
  'Authorization': 'Bearer your-api-key',
  'Property': 'myhotel',
  'Username': 'booking_engine',
  'Content-Type': 'application/json'
};

// Check availability
async function getAvailability(startDate, endDate) {
  const url = `${API_BASE}/availabilities?startDate=${startDate}&endDate=${endDate}`;
  const response = await fetch(url, { headers });
  if (!response.ok) throw new Error(`Failed: ${response.status}`);
  return await response.json();
}

// Create reservation
async function createReservation(bookingData) {
  const response = await fetch(`${API_BASE}/reservations`, {
    method: 'POST',
    headers,
    body: JSON.stringify(bookingData)
  });
  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error || 'Booking failed');
  }
  return await response.json();
}

// Guest portal login
async function guestLogin(uuid, lastName) {
  const response = await fetch(`${API_BASE}/guest-portal/login`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ uuid, lastName })
  });
  if (!response.ok) throw new Error('Login failed');
  return await response.json();
}

API Limitations

  • Booking-engine reservations are create-only: The public (API-key) booking flow can only create reservations. Modifications and cancellations are not available through the API-key endpoints. Note that the guest portal does expose guest-initiated changes — cancel, change dates, and change guests — but only for the authenticated guest's own reservations
  • No real-time catalog updates: Room type and rate changes are not pushed. Poll the endpoints periodically
  • Payment processing: Pay with HotelBee Payments via Stripe (/create-payment-intent + stripePaymentIntentId) or, if enabled for your key, your own gateway (externalPayment). See Payment Paths
  • Property scope: Your key can only be used with the property (or properties) it was issued for; any other Property header is rejected with 403
  • OTA prices require Channex: The /ota-prices endpoint only works with an active Channex integration
  • Guest portal scope: Guests can only view and act on their own reservations, invoices, orders, and messages

Support

For API support, technical questions, or to request API credentials:

Email: support@hotelbee.co

Response Time: We typically respond within 24 hours on business days

Common Questions

Q: How do I get API credentials?

A: Contact support@hotelbee.co with your property name and intended use case.

Q: How does the guest portal login work?

A: Guests authenticate with the reservation UUID (sent in their confirmation email) and their last name. This returns a JWT token valid for 7 days.

Q: Can guests check in online?

A: Yes, for APPROVED reservations where the check-in date is today or in the future. The check-in endpoint updates guest details and changes reservation status to CHECKIN.

Q: How are extra guest charges calculated?

A: Each room type has an includedCapacity (guests included in base price) and capacity tiers (extra charges per guest type). If the number of guests exceeds included capacity, extra charges are added per night.

Q: What is the difference between roomtypes pricing and rates pricing?

A: Room types have a default base price. Rates provide alternative pricing that may vary by date, season, or booking conditions (minimum nights, advance booking, etc.). Rates marked with bookingEngineRate: true are available to the booking engine.

Q: How do promo codes work?

A: Pass the code as promoCode on POST /reservations (and POST /quote). The server re-validates it against the property's promo codes and applies the discount. Optionally call POST /promocode/validate first to show the guest the discount before they book.

Q: Can I integrate with multiple properties?

A: Your key is limited to the property (or properties) it was issued for. To add a property, ask support to enable it for your key. Always send the correct Property header with each request.