Skip to main content

Attributes by Category

The attributes JSONB on value_added_service is a Jackson-polymorphic record. The category field on the parent row is the discriminator that selects the concrete type. Every shape below shows the JSON payload and the canonical example from production lingo.

The full list of categories:

CategoryJava typeUse for
FOODFoodAttributesBBQ, snacks, breakfast, high tea, dinner combos
EXPERIENCEExperienceAttributesBonfire, movie night, rain dance, DJ night
TRANSPORTTransportAttributesSedan, SUV, tempo traveller with hours + km
CHEFChefAttributesPersonal chef, master chef; with grocery on actuals
WELLNESSWellnessAttributesSpa, massage, yoga sessions
OTHEROtherAttributesFree-form escape hatch — avoid where possible

FOOD — FoodAttributes

Use for any meal or food add-on. Distinguishes meal slot, veg/non-veg counts, and explicit includes/excludes (so "Only Snacks (no BBQ)" is queryable, not buried in the name).

Schema

{
"category": "FOOD",
"mealType": "BREAKFAST | LUNCH | DINNER | HIGH_TEA | SNACKS",
"vegCount": 2,
"nonVegCount": 2,
"snackCount": 4,
"includes": ["BBQ", "Salad", "Dessert"],
"excludes": ["BBQ"],
"cuisines": ["Indian", "Continental"]
}

Example: "BBQ (2 Veg & 2 Non-Veg)"

{
"id": "BBQ_2V_2NV",
"name": "BBQ (2 Veg & 2 Non-Veg)",
"category": "FOOD",
"kind": "SINGLE",
"attributes": {
"category": "FOOD",
"mealType": "DINNER",
"vegCount": 2,
"nonVegCount": 2,
"includes": ["BBQ", "Marinade", "Sides"],
"cuisines": ["Indian"]
}
}

Example: "Only Snacks (no BBQ)"

{
"id": "SNACKS_NO_BBQ",
"name": "Only Snacks (no BBQ)",
"category": "FOOD",
"kind": "SINGLE",
"attributes": {
"category": "FOOD",
"mealType": "SNACKS",
"snackCount": 4,
"includes": ["Pakoras", "Sandwiches", "Chips"],
"excludes": ["BBQ"]
}
}

Example: "Breakfast & High Tea"

{
"id": "BREAKFAST_HIGH_TEA",
"name": "Breakfast & High Tea",
"category": "FOOD",
"kind": "BUNDLE",
"bundlePricingMode": "ROLLUP",
"attributes": {
"category": "FOOD",
"includes": ["Breakfast Buffet", "Evening High Tea"]
}
}

For a true breakfast-plus-high-tea bundle, you'd model it as kind=BUNDLE with two child VAS rows (one per meal) wired through vas_bundle_item. See Variants, bundles & choices.


EXPERIENCE — ExperienceAttributes

Use for non-food activities. Captures the activity type, duration, indoor/outdoor, and setup location on the property.

Schema

{
"category": "EXPERIENCE",
"experienceType": "BONFIRE | MOVIE_NIGHT | RAIN_DANCE | POOL_PARTY | DJ_NIGHT | OTHER",
"durationMinutes": 120,
"isOutdoor": true,
"location": "POOLSIDE | LAWN | ROOFTOP | INDOOR | GARDEN",
"setupNotes": "Includes projector, screen and seating for 10"
}

Example: "Bonfire"

{
"id": "BONFIRE",
"name": "Bonfire",
"category": "EXPERIENCE",
"kind": "SINGLE",
"attributes": {
"category": "EXPERIENCE",
"experienceType": "BONFIRE",
"durationMinutes": 120,
"isOutdoor": true,
"location": "LAWN",
"setupNotes": "Firewood, marshmallows, blankets"
}
}

Example: "Movie Night"

{
"id": "MOVIE_NIGHT",
"name": "Movie Night",
"category": "EXPERIENCE",
"kind": "SINGLE",
"attributes": {
"category": "EXPERIENCE",
"experienceType": "MOVIE_NIGHT",
"durationMinutes": 150,
"isOutdoor": false,
"location": "INDOOR",
"setupNotes": "Projector, screen, popcorn, seating for 8"
}
}

Example: "Rain Dance"

{
"id": "RAIN_DANCE",
"name": "Rain Dance",
"category": "EXPERIENCE",
"kind": "SINGLE",
"attributes": {
"category": "EXPERIENCE",
"experienceType": "OTHER",
"durationMinutes": 90,
"isOutdoor": true,
"location": "POOLSIDE"
},
"constraints": {
"minPax": 6,
"leadTimeHours": 48,
"seasons": ["SUMMER", "MONSOON"]
}
}

TRANSPORT — TransportAttributes

Use for vehicle rentals and transfers. Captures vehicle class/model, base envelope (hours + km), passenger capacity, and whether driver/fuel are included. Overage rates belong in pricing_config (BasePlusOveragePricing), not here.

Schema

{
"category": "TRANSPORT",
"vehicleClass": "HATCHBACK | SEDAN | SUV | TEMPO_TRAVELLER | LUXURY",
"vehicleModel": "SWIFT_DZIRE",
"vehicleLabel": "Premium Sedan - Swift Dzire",
"baseHours": 4,
"baseKm": 40,
"maxPassengers": 4,
"driverIncluded": true,
"fuelIncluded": true
}

Example: "Premium Sedan – Swift Dzire, 4 Hours / 40 KMs"

Modelled as a VARIANT_PARENT so the same vehicle can be offered with multiple hour/km envelopes. The variant carries the same shape with its own numbers.

{
"id": "PREMIUM_SEDAN",
"name": "Premium Sedan",
"category": "TRANSPORT",
"kind": "VARIANT_PARENT",
"attributes": {
"category": "TRANSPORT",
"vehicleClass": "SEDAN",
"vehicleModel": "SWIFT_DZIRE",
"vehicleLabel": "Premium Sedan - Swift Dzire",
"maxPassengers": 4,
"driverIncluded": true,
"fuelIncluded": true
}
}

Variant SWIFT_DZIRE_4H_40KM (see variants doc):

{
"id": "SWIFT_DZIRE_4H_40KM",
"vasId": "PREMIUM_SEDAN",
"name": "Swift Dzire - 4 Hours / 40 KMs",
"attributes": {
"category": "TRANSPORT",
"vehicleClass": "SEDAN",
"vehicleModel": "SWIFT_DZIRE",
"baseHours": 4,
"baseKm": 40
}
}

CHEF — ChefAttributes

Use for in-villa chefs. The groceryOnActuals flag pairs with OnActualsPricing in pricing_config for "Chef (Grocery on Actual)".

Schema

{
"category": "CHEF",
"chefType": "PERSONAL | GROUP | MASTER | SPECIALITY",
"cuisines": ["Indian", "Chinese"],
"durationHours": 8,
"mealCount": 3,
"groceryOnActuals": true
}

Example: "Chef (Grocery on Actual)"

{
"id": "CHEF_GROCERY_ACTUAL",
"name": "Chef (Grocery on Actual)",
"category": "CHEF",
"kind": "SINGLE",
"attributes": {
"category": "CHEF",
"chefType": "PERSONAL",
"cuisines": ["Indian", "Continental"],
"durationHours": 10,
"mealCount": 3,
"groceryOnActuals": true
}
}

Pair this with pricing_config = OnActualsPricing (see Pricing) so the booking is taken at zero and grocery cost is reconciled post-stay.


WELLNESS — WellnessAttributes

Use for spa, massage, yoga, ayurveda. Minimal shape — wellness type, duration, whether a certified therapist is bundled.

Schema

{
"category": "WELLNESS",
"wellnessType": "SPA | MASSAGE | YOGA | MEDITATION | AYURVEDA",
"durationMinutes": 60,
"therapistIncluded": true
}

Example: "In-Villa Massage"

{
"id": "MASSAGE_60",
"name": "In-Villa Massage (60 min)",
"category": "WELLNESS",
"kind": "SINGLE",
"attributes": {
"category": "WELLNESS",
"wellnessType": "MASSAGE",
"durationMinutes": 60,
"therapistIncluded": true
}
}

OTHER — OtherAttributes

Escape hatch for categories that haven't been modelled yet. Holds a free-form properties map. Avoid in new work — add a typed subtype instead. Useful only to onboard a service quickly and migrate later.

Schema

{
"category": "OTHER",
"properties": {
"freeFormKey": "freeFormValue"
}
}

Example

{
"id": "PHOTOGRAPHER",
"name": "Photo Session",
"category": "OTHER",
"kind": "SINGLE",
"attributes": {
"category": "OTHER",
"properties": {
"hours": 2,
"edited_photos": 25,
"drone_included": true
}
}
}

Constraints (VasConstraints)

Independent of category — applied uniformly across all VAS shapes. Stored in value_added_service.constraints JSONB. All fields are optional.

Schema

{
"minPax": 4,
"maxPax": 20,
"leadTimeHours": 24,
"seasons": ["WINTER"],
"daysOfWeek": [5, 6, 7],
"availableFrom": "18:00",
"availableUntil": "23:00",
"minBookingValue": 5000.00
}

daysOfWeek follows ISO-8601 (1=Monday … 7=Sunday). seasons is a free-form list of tags; the booking pipeline checks the booking date against the property's season calendar.

Example: pin Rain Dance to weekend summers, 6 guests minimum

{
"minPax": 6,
"leadTimeHours": 48,
"seasons": ["SUMMER", "MONSOON"],
"daysOfWeek": [5, 6, 7],
"availableFrom": "16:00",
"availableUntil": "20:00"
}