Skip to main content

Data Models

This document describes the key data models used throughout Bundleport APIs. For complete schemas, see the API Reference.

Hotel

Represents a hotel property.

interface Hotel {
code: string; // Bundleport hotel code
name: string; // Hotel name
descriptions?: HotelDescription[]; // Multi-language descriptions
category?: string; // Category code
starRating?: number; // 1-5 stars
location: Location; // Address and coordinates
amenities?: Amenity[]; // Hotel amenities
media?: Media[]; // Images and videos
contact?: Contact; // Contact information
providerCode?: string; // Provider identifier
providerHotelCode?: string; // Provider-specific code
destinationCode?: string; // Destination code
boardCodes?: string[]; // Available board types
rooms?: RoomData[]; // Available room types
}

Room

Represents a room type.

interface Room {
code: string; // Bundleport room code
name: string; // Room name
descriptions?: Text[]; // Multi-language descriptions
maxOccupancy: number; // Maximum guests
bedType?: string; // Type of bed
sizeSqM?: number; // Room size in square meters
images?: RoomImage[]; // Room images
occupancy?: Occupancy; // Occupancy details
beds?: Bed[]; // Bed configuration
providerCode?: string; // Provider identifier
providerRoomCode?: string; // Provider-specific code
}

Option

Represents a bookable hotel option from a search.

interface Option {
optionRefId: string; // Unique identifier for this option
hotel: Hotel; // Hotel information
rooms: RoomOption[]; // Room options with pricing
price: Price; // Total price
cancelPolicy?: CancelPolicy; // Cancellation policy
provider: ProviderInfo; // Provider details
}

RoomOption

A specific room option within an Option.

interface RoomOption {
description: string; // Room description
boardCode: string; // Board type (RO, BB, HB, FB, AI)
price: Price; // Room price
cancelPolicy?: CancelPolicy; // Room-specific cancellation policy
occupancyRefId: number; // Reference to occupancy configuration
}

Price

Pricing information with markup and margin details.

There are three independent prices in every response, plus the margin fields that describe your org markup:

interface Price {
currency: string; // ISO 4217 currency code
net: number; // Provider cost (what you pay the supplier)
binding?: boolean; // Provider binding flag (see note below)
suggested?: number; // Provider suggested / minimum selling price
gross?: number; // Provider retail (= suggested, or net if absent)
markupGross: number; // Your selling price = gross + org margin
markupNet: number; // Provider net cost + org margin amount
markupCurrency: string; // Markup currency
markupBinding: boolean; // If markup price must be respected
marginAmount?: number; // Org margin amount (markupGross - gross)
marginPercent?: number; // Effective margin percentage (on gross)
marginType?: string; // "FIXED" or "PERCENTAGE"
breakdown?: PriceBreakdown;
}

The three prices

PriceFieldWhere it comes from
CostnetThe provider — what the agency pays the supplier
Provider retailgross / suggestedThe provider — recommended / minimum selling price
Your selling pricemarkupGrossgross + the margin your org configures per connection

net and gross/suggested come straight from the provider. markupGross is the only price Bundleport computes, by adding your org margin on top of the provider gross — never on net.

markupGross = gross + orgMargin       // PERCENTAGE: gross × (1 + rate); FIXED: gross + amount
marginAmount = markupGross - gross

Price Fields

FieldDescription
netProvider cost — what you pay the supplier
suggestedProvider's suggested or minimum selling price
grossProvider retail price (= suggested, or net if the provider sends none)
markupGrossYour selling price = provider gross + org margin
marginAmountOrg margin amount (markupGross - gross)
marginPercentEffective margin percentage (applied on gross)
Which price to use?
  • Customer display: Use markupGross
  • Cost calculations: Use net
  • Margin analysis: Use marginAmount and marginPercent
When does markupGross equal gross?
  • No margin configured: markupGross = gross and a NO_MARKUP_CONFIGURED warning is returned.
  • skipMarkup enabled: When additionalParams.skipMarkup: "true" is passed, markupGross = gross.

In both cases your selling price is the provider retail price, with no org margin added.

Binding (minimum selling price)

When binding: true, the provider gross/suggested is a minimum selling price: you must not sell below it. Binding only limits how low you can go — it does not change how the margin or commission is calculated. Your markupGross (gross + margin) is always ≥ the binding price, so it stays compliant.

Two ways to read the total

The same total can be split two different ways, depending on what you want to show. They answer different questions, so the two splits do not match each other — only the total is shared.

1. Selling split — what the guest pays, broken into room vs taxes/fees. This is what UIs show in the price breakdown:

Total (gross / suggested)        77.52
├─ Stay amount (room, ex-tax) 69.88 = total − taxes/fees
└─ Taxes and fees 7.64 = sum of included tax/fee surcharges

2. Cost split — what the agency pays vs the provider's own margin. This is the cost view (e.g. the agency-cost tooltip):

Total (gross / suggested)        77.52
├─ Net (your cost) 71.14
└─ Provider margin 6.38 = gross − net (a.k.a. marketing fee)
Why Stay amountNet

They live in different splits:

  • Stay amount (69.88) is on the selling side, with taxes/fees removed.
  • Net (71.14) is the cost side, and still contains its own share of taxes.

They come from subtracting different things from the same total (total − taxes vs total − provider margin). Any closeness between the two numbers is a coincidence, not a relationship. There is no "net excluding taxes" exposed by the provider, so the two breakdowns never reconcile beyond the total.

When the provider sends only a net price (no separate suggested), then net = gross = suggested, the provider margin is 0, and the cost split collapses — only the selling split remains.

Markup Configuration

Markups define how prices are adjusted before being returned in API responses.

Markup Types

TypeDescriptionExample
PERCENTAGEPercentage of provider gross5% → gross × 1.05
FIXED_AMOUNTFixed amount added to provider gross10€ → gross + 10

Markup Priority

When multiple markups exist for a connection, only the highest priority markup is applied:

  1. Priority field (higher values win)
  2. Creation date (newer wins if same priority)
One Markup Per Connection

Configure only one global markup per connection to ensure consistent pricing across all prices (option price, room prices). Having multiple markups may cause confusion.

Markup Levels

Markups can be configured at different levels:

LevelDescription
ORGANIZATIONBase level, applies to all bookings
AGENCYMid level, applies to specific agency
USERHighest level, personalized for specific user

Lower levels override higher levels: USER > AGENCY > ORGANIZATION

PriceBreakdown

Detailed price components.

interface PriceBreakdown {
base: number; // Base room price
taxes?: number; // Taxes
fees?: number; // Fees
supplements?: Supplement[]; // Additional charges
discounts?: Discount[]; // Applied discounts
}

Occupancy

Guest configuration.

interface Occupancy {
adults: number; // Number of adults
children?: number; // Number of children
childrenAges?: number[]; // Ages of children (required if children > 0)
total: number; // Total guests (adults + children)
}

Stay

Date range for hotel stay.

interface Stay {
checkIn: string; // Check-in date (YYYY-MM-DD)
checkOut: string; // Check-out date (YYYY-MM-DD)
nights?: number; // Number of nights (calculated)
}

Destination

Location where hotels are located.

interface Destination {
code: string; // Destination code
name: string; // Destination name
type: string; // Type: city, region, airport, etc.
location?: Location; // Geographic location
parent?: string; // Parent destination code
children?: string[]; // Child destination codes
texts?: Text[]; // Multi-language descriptions
}

Location

Geographic location and address.

interface Location {
address?: string; // Street address
city?: string; // City name
state?: string; // State or province
countryCode?: string; // ISO 3166-1 alpha-2 country code
postalCode?: string; // Postal/ZIP code
latitude?: number; // Latitude coordinate
longitude?: number; // Longitude coordinate
}

CancelPolicy

Cancellation policy details.

interface CancelPolicy {
refundable: boolean; // Can be cancelled for free?
cancelPenalties?: CancelPenalty[]; // Penalty rules
description?: string; // Human-readable description
}

interface CancelPenalty {
penaltyType: 'NIGHTS' | 'PERCENT' | 'IMPORT'; // Penalty calculation type
value: number; // Penalty amount
deadline: string; // ISO 8601 deadline (before this date)
currency?: string; // Currency for IMPORT type
}

Booking

A confirmed or pending reservation.

interface Booking {
id: string; // Bundleport booking ID
status: BookingStatus; // Current status
reference: BookingReference; // Booking references
hotel: Hotel; // Hotel information
stay: Stay; // Check-in/check-out dates
rooms: BookingRoom[]; // Booked rooms
holder: Holder; // Booking holder information
price: Price; // Final price
cancelPolicy?: CancelPolicy; // Cancellation policy
remarks?: string[]; // Booking remarks
createdAt: string; // ISO 8601 creation timestamp
updatedAt: string; // ISO 8601 last update timestamp
}

type BookingStatus =
| 'CONFIRMED'
| 'PENDING'
| 'ON_REQUEST'
| 'CANCELLED'
| 'MODIFIED'
| 'FAILED';

BookingReference

References for a booking.

interface BookingReference {
bookingID: string; // Bundleport booking ID
clientReference?: string; // Your internal reference
providerReference?: string; // Provider confirmation number
confirmationNumber?: string; // Hotel confirmation number
}

BookingRoom

A room within a booking.

interface BookingRoom {
description: string; // Room description
boardCode: string; // Board type
paxes: Pax[]; // Guest information
confirmationReference?: string; // Room-specific confirmation
price?: Price; // Room price
}

Holder

Booking holder (main guest).

interface Holder {
name: string; // First name
surname: string; // Last name
email: string; // Email address
phone?: string; // Phone number
nationality?: string; // ISO 3166-1 alpha-2 country code
}

Pax

Guest information.

interface Pax {
name: string; // First name
surname: string; // Last name
age?: number; // Age (for children)
type?: 'ADULT' | 'CHILD'; // Guest type
}

Tracing

Request processing visibility.

interface Tracing {
status: 'OK' | 'PARTIAL' | 'ERROR'; // Overall status
accessSpans: AccessSpan[]; // Per-provider details
processTime?: number; // Processing time in milliseconds
}

interface AccessSpan {
access: string; // Access ID (provider identifier)
status: 'OK' | 'ERROR' | 'TIMEOUT';
hotelsRequested?: number; // Hotels requested from provider
hotelsReturned?: number; // Hotels returned by provider
errorCode?: string; // Error code if status is ERROR
errorDescription?: string; // Error description
processTime?: number; // Provider response time
}

Warning

Non-fatal issue that doesn't prevent request success.

interface Warning {
code: string; // Warning code (e.g., "WARN_CODE_NONE")
description: string; // Human-readable description
connectionCode?: string; // Connection code associated with warning
additionalData?: Record<string, string>; // Additional context data
}

Common Warning Types

Warning TypeDescription
PARTIAL_RESPONSEOne or more providers failed or timed out
PRICE_CHANGEDPrice changed since search
NO_MARKUP_CONFIGUREDConnection has no margin; selling price uses provider gross/suggested

Example warning for no markup configured:

{
"code": "WARN_CODE_NONE",
"description": "No markup configured for connection; selling price uses provider gross/suggested",
"connectionCode": "testb-hbds-1876",
"additionalData": {
"warning_type": "NO_MARKUP_CONFIGURED",
"connection_code": "testb-hbds-1876"
}
}

Error

Error information.

interface Error {
code: string; // Error code
type: 'CLIENT' | 'SERVER'; // Error type
message: string; // Human-readable message
description?: string; // Detailed description
details?: Record<string, any>; // Additional error details
field?: string; // Related field (if applicable)
}

Common Field Types

Text

Multi-language text content.

interface Text {
type?: string; // Text type (description, shortDescription, etc.)
languageCode: string; // ISO 639-1 language code
text: string; // Text content
}

Media

Image or video media.

interface Media {
url: string; // Media URL
mediaType?: 'IMAGE' | 'VIDEO'; // Media type
caption?: string; // Caption
width?: number; // Width in pixels
height?: number; // Height in pixels
orderIndex?: number; // Display order
isPrimary?: boolean; // Is primary image?
}

Amenity

Hotel or room amenity.

interface Amenity {
code: string; // Amenity code
name?: string; // Amenity name
type?: string; // Amenity type/category
description?: string; // Description
}

Next Steps