Frontend integration contract for itinerary pricing flows.
PUT /v1/itinerary/:id/pricing-selection (admin) is deprecated/removed from admin flow.PUT /v1/itinerary/:id/pricing-table for pricing updates.POST /v1/bookings via required pricingSelection payload.PUT /v1/itinerary/:id/customer-pricing-selection has been removed.POST /v1/bookings under pricingSelection./v1/itineraryPricing categories use HotelCategoryEnum numeric values (src/shared/enums/hotel-category.enum.ts):
1 = LUXURY2 = PREMIUM3 = SUPER_DELUXE4 = DELUXE5 = STANDARDMarkup type values (MarkupTypeEnum):
1 = FLAT2 = PERCENTAGEDiscount type values (DiscountTypeEnum, when present in summary):
1 = FLAT2 = PERCENTAGE/v1/itinerary/:id/price-detailsSource of truth for pricing table mode, defaults, and summary.
adults (number)children (number)extraMattressCount (number; pricing-only, excluded from traveler/slab count)selectedCategory (number enum value; applied in customer/b2c flow)selectedTransferId (number)selectedVehicleName (string)selectedCapacityMin (number)selectedCapacityMax (number)includeDinnerSupplement (true/false)channel (b2b or b2c)GET /price-details recalculates summary from the supplied query params (selection context) in real time.
Compatibility: legacy query params childrenWithoutBed and childrenWithBed are still accepted as aliases for children and extraMattressCount.
{
"message": "SUC_PRICE_DETAILS_FETCHED",
"data": {
"itineraryId": 958,
"pricingTableType": "hotel_transfer",
"mappingSnapshot": {
"hasActivities": true,
"hasHotels": true,
"hasTransfers": true
},
"defaultCategory": 5,
"availableCategories": [1, 2, 3, 4, 5],
"selection": {
"selectedCategory": 5,
"selectedVehicleSlab": {
"transferId": 3002,
"vehicleName": "Innova / Xylo / Similar",
"capacityMin": 1,
"capacityMax": 3
},
"travelerDetails": {
"adults": 2,
"children": 1,
"infants": 0
},
"extraMattressCount": 0,
"includeDinnerSupplement": false,
"displayMode": 1
},
"pricingTable": {},
"summary": {
"channel": "b2b",
"currency": "INR",
"baseCost": 0,
"gstPercent": 5,
"gstAmount": 0,
"tcsPercent": 0,
"tcsAmount": 0,
"discountType": 1,
"discountValue": 0,
"discountAmount": 0,
"grandTotal": 0,
"tcsApplicable": false,
"markupPercent": 5,
"markupType": 2,
"markupValue": 5,
"markupAmount": 0
}
}
}
pricingTable by scenarioactivities_only{
"pricingTableType": "activities_only",
"pricingTable": {
"activitiesOnly": {
"adultPrice": 5000,
"childPrice": 2000
}
}
}
hotel_only{
"pricingTableType": "hotel_only",
"pricingTable": {
"categoryPerPersonPrices": [
{
"category": 5,
"perPersonPrice": 15100,
"primaryMappedHotelName": "Taj City Center"
},
{
"category": 2,
"perPersonPrice": 18400,
"primaryMappedHotelName": null
}
],
"hotelRows": [
{
"category": 5,
"pricePerPerson": 15100,
"plan": "CP",
"extraBedPrice": 4050,
"childWithoutBedPrice": 2100,
"dinnerSupplementPrice": 1950
},
{
"category": 2,
"pricePerPerson": 18400,
"plan": "CP",
"extraBedPrice": 5500,
"childWithoutBedPrice": 3800,
"dinnerSupplementPrice": 2400
}
]
}
}
transfer_only{
"pricingTableType": "transfer_only",
"pricingTable": {
"transferRows": [
{
"transferId": 3001,
"vehicleName": "Wagon R / Similar",
"capacityMin": 1,
"capacityMax": 3,
"pricePerPerson": 15100
},
{
"transferId": 3002,
"vehicleName": "Innova / Xylo / Similar",
"capacityMin": 1,
"capacityMax": 3,
"pricePerPerson": 18400
},
{
"transferId": 3002,
"vehicleName": "Innova / Xylo / Similar",
"capacityMin": 4,
"capacityMax": 6,
"pricePerPerson": 12350
}
]
}
}
hotel_transfer{
"pricingTableType": "hotel_transfer",
"pricingTable": {
"categoryPerPersonPrices": [
{
"category": 5,
"perPersonPrice": 15100,
"primaryMappedHotelName": "Taj City Center"
}
],
"rows": [
{
"category": 5,
"plan": "CP",
"extraBedPrice": 4050,
"childWithoutBedPrice": 2100,
"dinnerSupplementPrice": 1950,
"vehiclePricing": [
{
"transferId": 3001,
"vehicleName": "Wagon R / Similar",
"capacityMin": 1,
"capacityMax": 3,
"pricePerPerson": 15100
},
{
"transferId": 3002,
"vehicleName": "Innova / Xylo / Similar",
"capacityMin": 1,
"capacityMax": 3,
"pricePerPerson": 18400
},
{
"transferId": 3002,
"vehicleName": "Innova / Xylo / Similar",
"capacityMin": 4,
"capacityMax": 6,
"pricePerPerson": 12350
}
]
}
]
}
}
Customer write path:
POST /v1/bookings (booking-jwt)pricingSelection payloadbooking.priceDetails)pricingSelection block inside booking payload){
"channel": "b2c",
"selectedCategory": 5,
"selectedVehicleSlab": {
"transferId": 3002,
"vehicleName": "Innova / Xylo / Similar",
"capacityMin": 1,
"capacityMax": 3
},
"travelerDetails": {
"adults": 2,
"children": 1,
"infants": 0
},
"extraMattressCount": 0,
"includeDinnerSupplement": true,
"displayMode": 1
}
Booking response includes booking.priceDetails in same structure as GET /price-details with recalculated summary.
Notes:
selectedCategory is meaningful for hotel-involved flows.selectedVehicleSlab is meaningful for transfer-involved flows.pricingSelection.travelerDetails.childrenWithoutBed and pricingSelection.travelerDetails.childrenWithBed are accepted as legacy aliases.vehiclePricing[] and transferRows[] include transferId for transfer reference binding.pricingSelection.travelerDetails, children and infants are optional; pricing computation uses adults + children for traveler/slab count.extraMattressCount is pricing-only and must be sent outside travelerDetails.hotel_transfer and hotel_only, pricingTable.categoryPerPersonPrices provides category-wise per-person prices (markup included, dinner supplement excluded) for category card display, plus primaryMappedHotelName for that category.hotel_only, each pricingTable.hotelRows[] now also includes pricePerPerson for table column rendering.b2c channel for hotel_transfer and hotel_only, categories with perPersonPrice = 0 are omitted from categoryPerPersonPrices.primaryMappedHotelName is null.transferId; vehicle name is resolved from transfer master.1-34-seateractivities_only, customer selection does not accept activity prices; prices come from admin pricing-table config.pricingTable.rows (or any rows) in pricingSelection; selection inputs only./v1/itinerary/:id/pricing-table (admin only)Upsert pricing master rows (rate card) for each scenario.
TRIP permissionhotel_transfer payload{
"scenario": "hotel_transfer",
"rows": [
{
"category": 5,
"plan": "CP",
"extraBedPrice": 4050,
"childWithoutBedPrice": 2100,
"dinnerSupplementPrice": 1950,
"vehiclePricing": [
{
"transferId": 3001,
"capacityMin": 1,
"capacityMax": 3,
"pricePerPerson": 15100
}
]
}
]
}
hotel_only payload{
"scenario": "hotel_only",
"hotelRows": [
{
"category": 5,
"pricePerPerson": 15100,
"plan": "CP",
"extraBedPrice": 4050,
"childWithoutBedPrice": 2100,
"dinnerSupplementPrice": 1950
}
]
}
transfer_only payload{
"scenario": "transfer_only",
"category": 5,
"transferRows": [
{
"transferId": 3001,
"capacityMin": 1,
"capacityMax": 3,
"pricePerPerson": 12000
}
]
}
category is optional for transfer_only; if omitted, backend resolves category from: dto.category → basePriceDetails.defaultCategory → basePriceDetails.availableCategories[0] → HotelCategoryEnum.STANDARD (5).
activities_only payload{
"scenario": "activities_only",
"displayMode": 1,
"activitiesOnly": {
"adultPrice": 5000,
"childPrice": 2000
}
}
{
"message": "SUC_PRICING_TABLE_SAVED",
"data": {
"itineraryId": 958,
"pricingTableType": "hotel_transfer",
"pricingTable": {},
"selection": {},
"summary": {}
}
}
Notes:
POST /bookings pricingSelection updates customer selection inputs and recalculates totals.channel = "b2b" (default when channel is omitted)tripType (normalizeTripTypeToCategory); selectedCategory query param is ignored for summary computation.summary includes markupPercent, markupType, markupValue, markupAmount as separate fields in addition to gstAmount, tcsAmount, discountAmount.summary.baseCost = raw base cost before markup.channel = "b2c"selectedCategory) is honored; defaults to itinerary tripType or first available category.hotel_transfer, selected category drives transfer slab lookup from that category's bundle rows.summary.baseCost = post-markup amount (markup has been folded into the base).markupPercent, markupType, markupValue, markupAmount are not included in summary.gstAmount, tcsAmount, discountAmount are still returned separately.gstPercent and gstAmount are null (not 0) when no GST is configured.categoryPerPersonPrices rows where perPersonPrice = 0 are omitted in b2c response./v1/itinerary/price-details/:leadId/:tripIdPublic lead pricing endpoint.
/v1/itinerary/:id/customer-price-detailsCustomer-token variant of itinerary price-details endpoint.
/v1/itinerary/:id/travelersSaves the default traveler/pax configuration on the itinerary (adults, children, infants, selectedCategory, selectedVehicleSlab, extraMattressCount, includeDinnerSupplement, displayMode). These defaults are used when displaying a trip with no customer-supplied selection context.
This endpoint remains active. For customer booking price snapshots, use POST /bookings with pricingSelection.
tripType is the canonical category source for b2b channel. For b2c, selectedCategory from query/selection is used.hotel_transfer and hotel_only, availableCategories in the response contains all HotelCategoryEnum values [1,2,3,4,5] sorted ascending — not just those with pricing rows. Filter categoryPerPersonPrices to find which categories actually have pricing.transfer_only and activities_only, availableCategories is derived from existing bundle pricing categories + tripType.PERCENTAGE (type 2) and FLAT (type 1) markup typesMarkup entity with category = 'gst':null in summary)IN).adults + children count (extraMattressCount and infants excluded from slab banding):capacityMin ≤ totalPax ≤ capacityMaxhotel_transfer: selected/default category hotel bundle + primary transfer vehicle slab.hotel_only: selected/default category hotel bundle.transfer_only: primary transfer pricing slabs.activities_only: adultPrice and childPrice from travelerDetails.hotel + transfer (+activities):hotel-only:hotel_only and hotel_transfer (checked only when saving pricing table on a published itinerary):pricePerPerson in the submitted pricing payload, a hotel must be mapped to that itinerary for every unique locationId (day city) under that category. Checked in validatePublishedItineraryPricingCoverage().transfer-only:activities-only:adultPrice is mandatory