lifestyle.book_gym_session — Full Intent Specification
INTENT NAMESPACE: lifestyle
INTENT NAME: book_gym_session
FULL ID: lifestyle.book_gym_session
VERSION: v1.0.0
STATUS: draft
LAST UPDATED: 2026-05-10
TTBS WEIGHTS: time 0.30 · taste 0.30 · budget 0.30 · safety 0.10
Drop-in / pay-per-session gym visit. Inherits the slot-based service-provider shape from lifestyle.book_salon: every salon-shape field has a gym equivalent (gym instead of salon, equipment instead of services, trainer-on-floor instead of stylist). Distinct: (a) gym_kind enum (full_service_chain / boutique / co-working_gym / hotel_gym); (b) equipment_categories[] and class_kinds_offered; (c) trainer_assistance_kind (floor_walker / on_demand / paid_pt_session); (d) peak_hours data drives time signals; (e) shower + locker + parking are first-class amenities.
Inheritance contract: every field in lifestyle.book_salon §2/§4/§5 except cosmetics-specific (skin_kind, hair_kind, products) is REQUIRED here too — adapted to the gym domain.
1. NATURAL LANGUAGE COVERAGE
Classifies IN
- "drop-in gym session today"
- "1-day gym pass"
- "Cult.fit gym near me"
- "pay per use gym"
- "gym for travel day"
- "weight training session today"
- "cardio gym 1 hour"
- "Anytime Fitness drop-in"
- "boutique gym near office"
Classifies OUT — borderline NO
- "yoga class" →
lifestyle.book_yoga_class - "personal trainer" →
lifestyle.book_personal_trainer - "monthly gym membership" →
lifestyle.book_gym_membership - "wellness retreat" →
lifestyle.book_wellness_retreat - "spa" →
lifestyle.book_spa_treatment
MULTI-INTENT TRIGGERS
- "gym session and protein shake" →
lifestyle.book_gym_session+food.order_delivery - "gym pass and membership inquiry" →
lifestyle.book_gym_session+lifestyle.book_gym_membership
2. INPUT — TOMO → PROVIDER (DELTA over lifestyle.book_salon)
All input fields from lifestyle.book_salon §2 are REQUIRED. REPLACE services_wanted with workout_intent. ADAPT user_profile to fitness profile. ADD gym-specific fields.
{
"intent": "lifestyle.book_gym_session",
"intent_version": "v1.0.0",
"request_id": "req_gym_8h2k_2026-05-10T08:00:00Z",
"search_area": { ... }, // same as base
"scheduled_for_iso": "2026-05-10T18:30:00+05:30",
"duration_minutes": 90,
"session_kind": "drop_in_pass",
"user_fitness_profile": {
"gender": "female",
"age_band": "26-35",
"fitness_level": "intermediate",
"weight_kg": 58,
"height_cm": 165,
"medical_conditions_disclosed": [],
"fitness_goals": ["weight_loss", "tone_up"],
"current_workout_routine_kind": "mixed_cardio_strength"
},
"workout_intent": [
{
"category_kind": "weight_training",
"duration_minutes": 45
},
{
"category_kind": "treadmill_cardio",
"duration_minutes": 30
}
],
"preferences": {
"gym_kind_acceptable": ["chain_gym", "boutique_gym", "hotel_gym"],
"gym_kind_preferred": "chain_gym",
"trainer_floor_walker_required": false,
"trainer_on_demand_pt_required": false,
"shower_required": true,
"locker_required": true,
"ac_required": true,
"parking_required": true,
"vegan_smoothie_bar_required": false,
"single_gender_floor_required": false,
"budget_band": "good",
"budget_max_inr_total": 800,
"max_wait_minutes_for_equipment": 5,
"online_payment_required": true,
"trial_workout_buddy_optional": false
},
"context": {
... (same as base)
}
}
| Field | Type | Constraint | Notes |
|---|---|---|---|
intent |
string | REQUIRED, equals "lifestyle.book_gym_session" |
|
session_kind |
enum | REQUIRED, see §5 | |
duration_minutes |
int | REQUIRED, ≥30 | typical 60-120 |
user_fitness_profile.gender |
enum | REQUIRED | |
user_fitness_profile.age_band |
enum | REQUIRED | |
user_fitness_profile.fitness_level |
enum | REQUIRED, see §5 | drives gym-class filter |
user_fitness_profile.medical_conditions_disclosed |
array |
REQUIRED, may be empty | see §5 |
user_fitness_profile.fitness_goals |
array |
REQUIRED, ≥1 | see §5 |
user_fitness_profile.current_workout_routine_kind |
enum | REQUIRED | |
workout_intent[].category_kind |
enum | REQUIRED, see §5 | |
workout_intent[].duration_minutes |
int | REQUIRED, ≥0 | |
preferences.gym_kind_acceptable |
array |
REQUIRED, ≥1 | see §5 |
preferences.trainer_floor_walker_required |
bool | REQUIRED | |
preferences.trainer_on_demand_pt_required |
bool | REQUIRED | |
preferences.shower_required |
bool | REQUIRED | |
preferences.locker_required |
bool | REQUIRED | |
preferences.parking_required |
bool | REQUIRED | |
preferences.single_gender_floor_required |
bool | REQUIRED | |
preferences.max_wait_minutes_for_equipment |
int | REQUIRED, ≥0 | |
preferences.trial_workout_buddy_optional |
bool | REQUIRED | first-timer support |
REMOVED from lifestyle.book_salon §2:
user_profile.skin_kind,hair_kind,hair_length,current_colorservices_wanted(replaced by workout_intent)preferences.products_kind_preferred,ammonia_free_color_required,vegan_products_only,halal_products_required
Anti-fabrication preamble (universal): no paid placement, no urgency text, no commission-influenced fields.
3. PROVIDER TOOLS (DELTA)
8 of 9 tools from lifestyle.book_salon §3 REQUIRED, with SalonOption replaced by GymSessionOption. ADD:
Tool 10: get_equipment_availability_now
PURPOSE: live equipment occupancy for the requested gym at slot time
INPUT: { gym_id, slot_iso, request_id }
OUTPUT: { categories: EquipmentAvailability[], updated_iso }
SLA: p95 < 800ms
All 10 REQUIRED.
4. RESPONSE SHAPE (DELTA)
GymSessionOption
All from SalonOption §4 REQUIRED, REPLACE salon with gym, services_offered with equipment_and_classes_catalog, stylists with trainers_on_floor. ADD gym-specific blocks.
gym:
id: string, REQUIRED
merchant_id: string, REQUIRED
name: string, REQUIRED
brand: string, REQUIRED
gym_kind: STRICT ENUM, REQUIRED # see §5
partner_account_age_days: int, REQUIRED, ≥0
total_sessions_30day: int, REQUIRED, ≥0
total_active_members: int, REQUIRED, ≥0
address:
line_1: string, REQUIRED
line_2: string, REQUIRED
neighborhood: string, REQUIRED
city: string, REQUIRED
pincode: string, REQUIRED
lat: float, REQUIRED
lng: float, REQUIRED
google_place_id: string, REQUIRED
landmark: string, REQUIRED
distance_from_user_km: float, REQUIRED
ratings:
overall_score: float, REQUIRED, 0-5
overall_count: int, REQUIRED, ≥0
recent_30day_score: float, REQUIRED, 0-5
cleanliness_score: float, REQUIRED, 0-5
equipment_quality_score: float, REQUIRED, 0-5
crowd_density_score: float, REQUIRED, 0-5 # less crowded = higher
trainer_helpfulness_score: float, REQUIRED, 0-5
on_time_30day_pct: float, REQUIRED, 0-1
operating_hours: # same shape as salon
is_open_now: boolean, REQUIRED
next_open_iso: ISO_DATETIME, REQUIRED
next_close_iso: ISO_DATETIME, REQUIRED
is_24x7: boolean, REQUIRED
hours_of_operation: array, REQUIRED, 7 entries
amenities:
air_conditioning: boolean, REQUIRED
music_system: boolean, REQUIRED
showers_count: int, REQUIRED, ≥0
showers_with_hot_water: boolean, REQUIRED
lockers_count: int, REQUIRED, ≥0
locker_secure_lock: boolean, REQUIRED
changing_rooms_count: int, REQUIRED, ≥0
parking_available: boolean, REQUIRED
parking_kind: STRICT ENUM, REQUIRED
smoothie_bar_present: boolean, REQUIRED
smoothie_vegan_options: boolean, REQUIRED
wifi_available: boolean, REQUIRED
wifi_free: boolean, REQUIRED
pool_available: boolean, REQUIRED # for fitness centers with pool
sauna_available: boolean, REQUIRED
steam_room_available: boolean, REQUIRED
physiotherapist_on_call: boolean, REQUIRED
childcare_available: boolean, REQUIRED
wheelchair_accessible: boolean, REQUIRED
female_only_floor: boolean, REQUIRED
male_only_floor: boolean, REQUIRED
cctv_in_workout_zones: boolean, REQUIRED
panic_button_at_floor: boolean, REQUIRED
trust:
municipal_license_number: string, REQUIRED
municipal_license_valid_until: ISO_DATE, REQUIRED
safety_audit_iso: ISO_DATETIME, REQUIRED
safety_audit_score: int, REQUIRED, 0-100
equipment_certified_safe: boolean, REQUIRED # last cert iso for safety
equipment_last_certified_iso: ISO_DATE, REQUIRED
employee_kyc_pct: int, REQUIRED, 0-100
insurance_coverage_inr: INR_INTEGER, REQUIRED
aed_defibrillator_present: boolean, REQUIRED
first_aid_kit_present: boolean, REQUIRED
fire_safety_compliant: boolean, REQUIRED
tomo_field_team_audited: boolean, REQUIRED
photos:
thumbnail_url: URL, REQUIRED
hero_url: URL, REQUIRED
workout_zones_photos_url: URL, REQUIRED
amenities_photos_url: URL, REQUIRED
ai_generated_photos: boolean, REQUIRED # MUST be false
equipment_and_classes_catalog: array<EquipmentItem>, REQUIRED, ≥3 # see §5
available_slots: array<GymSlotOption>, REQUIRED, ≥1
trainers_on_floor: array<TrainerProfile>, REQUIRED, may be empty
current_capacity:
current_members_in_premises: int, REQUIRED, ≥0
capacity_max: int, REQUIRED, ≥1
capacity_pct: int, REQUIRED, 0-100
peak_hours_today: array<string>, REQUIRED # time ranges as strings
session_pass:
pass_kind: STRICT ENUM, REQUIRED # see §5
pass_inr: INR_INTEGER, REQUIRED
pass_includes: array<STRICT ENUM>, REQUIRED # see §5
trainer_floor_walker_included: boolean, REQUIRED
pt_session_extra_inr: INR_INTEGER, REQUIRED # 0 if not requested
shower_included: boolean, REQUIRED
smoothie_voucher_included: boolean, REQUIRED
smoothie_voucher_inr: INR_INTEGER, REQUIRED
validity_days: int, REQUIRED, ≥1 # 1 typical for drop-in
# (all other fields from SalonOption §4 - fare, cancellation, freshness, _provider - REQUIRED)
EquipmentItem
equipment_id: string, REQUIRED
category_kind: STRICT ENUM, REQUIRED # see §5
equipment_name: string, REQUIRED # "Treadmill", "Squat Rack"
brand: string, REQUIRED
units_count: int, REQUIRED, ≥1
maintenance_score: int, REQUIRED, 0-100
last_certified_iso: ISO_DATE, REQUIRED
peak_wait_minutes: int, REQUIRED, ≥0
photo_url: URL, REQUIRED
photo_ai_generated: boolean, REQUIRED # MUST be false
TrainerProfile
(Subset of StylistProfile — drop hair-specific fields, add fitness specializations. All fields REQUIRED.)
trainer_id: string, REQUIRED
display_name: string, REQUIRED
photo_url: URL, REQUIRED
photo_ai_generated: boolean, REQUIRED # MUST be false
gender: STRICT ENUM, REQUIRED
age_band: STRICT ENUM, REQUIRED
experience_years: int, REQUIRED, ≥0
specializations: array<STRICT ENUM>, REQUIRED, ≥1 # see §5
certifications: array<STRICT ENUM>, REQUIRED, may be empty
certifying_authorities: array<STRICT ENUM>, REQUIRED, may be empty
languages_spoken: array<RFC_3066_LOCALE>, REQUIRED, ≥1
ratings:
rating_avg: float, REQUIRED, 0-5
rating_count: int, REQUIRED, ≥0
sessions_completed_total: int, REQUIRED, ≥0
sessions_completed_30day: int, REQUIRED, ≥0
kyc:
aadhaar_verified: boolean, REQUIRED
pan_verified: boolean, REQUIRED
background_check_passed: boolean, REQUIRED
background_check_iso: ISO_DATETIME, REQUIRED
employed_kind: STRICT ENUM, REQUIRED # full_time | part_time | freelance
on_floor_now: boolean, REQUIRED
pt_session_inr: INR_INTEGER, REQUIRED # if available for paid PT
GymSlotOption
slot_id: string, REQUIRED
slot_iso: ISO_DATETIME, REQUIRED
duration_minutes: int, REQUIRED
class_id: string, REQUIRED # may be empty for free-floor sessions
class_name: string, REQUIRED # "Spin class", "" for free-floor
trainer_id_assigned: string, REQUIRED # may be empty
spots_remaining: int, REQUIRED, ≥0
spots_total: int, REQUIRED, ≥1
arrival_window_minutes: int, REQUIRED
deposit_inr: INR_INTEGER, REQUIRED # 0 if no deposit
cancellation_until_iso: ISO_DATETIME, REQUIRED
no_show_charge_inr: INR_INTEGER, REQUIRED
slot_kind: STRICT ENUM, REQUIRED # see §5
freshness_iso: ISO_DATETIME, REQUIRED
Forbidden fields
paid_placement_score | sponsored_rank | promotion_priority |
fake_recent_session_text | auto_inflate_session_count |
ai_generated_photos (must equal false) | hidden_pass_fee |
fake_certifications | fake_aed_defibrillator_present |
fake_partner_completion_rate_30d | hidden_locker_charge
5. CONTROLLED VOCABULARIES (DELTA)
All non-cosmetic vocabularies from lifestyle.book_salon §5 REQUIRED.
session_kind
drop_in_pass | day_pass | trial_session | guest_pass |
class_only_pass | open_floor_only | premium_drop_in
gym_kind
chain_gym | boutique_gym | budget_gym | premium_independent |
hotel_gym | corporate_gym | women_only_gym | mens_only_gym |
24x7_gym | community_gym | celebrity_brand
user_fitness_profile.fitness_level
beginner | novice | intermediate | advanced | athlete | elite
user_fitness_profile.medical_conditions_disclosed[]
hypertension | diabetes | heart_condition | back_pain | knee_problem |
shoulder_problem | pregnancy | postpartum | hernia | arthritis |
asthma | recent_surgery | medication_dependent | other | none
user_fitness_profile.fitness_goals[]
weight_loss | weight_gain | muscle_gain | tone_up | endurance |
strength | flexibility | rehabilitation | sport_specific |
general_fitness | mental_wellbeing
user_fitness_profile.current_workout_routine_kind
none | mixed_cardio_strength | strength_only | cardio_only |
calisthenics | yoga_pilates | crossfit | sport_specific | other
workout_intent[].category_kind and EquipmentItem.category_kind
weight_training | strength_machines | free_weights | cardio_treadmill |
cardio_elliptical | cardio_bike | cardio_rower | cardio_stairmaster |
functional_training | crossfit_zone | trx_suspension | yoga_zone |
pilates_zone | spin_class | hiit_class | zumba_class | dance_class |
boxing_zone | mma_zone | kettlebell_zone | sled_zone |
recovery_zone | stretching_zone | sauna_steam | pool_lap_swim |
pool_aqua_aerobics | physio_zone
gym.amenities.parking_kind
valet_only | self_park_lot | street_parking | mall_parking |
no_parking | basement
session_pass.pass_kind
basic_floor | full_access | class_inclusive | premium_with_pt |
trial_first_visit | guest_with_member
session_pass.pass_includes[]
weight_training_floor | cardio_floor | functional_zone |
classes_unlimited_today | spin_class | hiit_class | yoga_class |
zumba_class | sauna_steam | pool_access | locker_access |
shower_access | smoothie_voucher
TrainerProfile.specializations[]
strength_coach | functional_trainer | crossfit_l1 | crossfit_l2 |
yoga_alliance_certified | pilates_certified | hiit_specialist |
zumba_licensed | spin_certified | nutrition_consultant |
postpartum_specialist | rehab_specialist | sport_coach
TrainerProfile.certifications[]
acsm_certified | nasm_certified | issa_certified | aceit_certified |
icb_l1 | icb_l2 | yoga_alliance_200 | yoga_alliance_500 |
pilates_l1 | pilates_l2 | nutrition_certified | partner_internal_cert
TrainerProfile.certifying_authorities[]
acsm | nasm | issa | acefit | crossfit_inc | yoga_alliance |
balanced_body_pilates | iyengar_yoga_institute | partner_internal
TrainerProfile.gender
female | male | non_binary | prefer_not_to_say
TrainerProfile.employed_kind
full_time_employee | part_time_employee | contract | freelance
GymSlotOption.slot_kind
walk_in_compatible | premium_slot | early_morning_5_to_7 |
mid_morning_7_to_10 | midday_10_to_4 | evening_peak_5_to_8 |
late_evening_8_plus | weekend_premium | women_only_hours
cancel_appointment.reason (additions)
medical_condition_arose | gym_closed_unexpectedly |
... (all base reasons)
merchant_id resolution order
1. Google Place ID
2. Municipal license number
3. partner_id + ":" + gym_id
6. TTBS DIMENSIONS (DELTA)
Per-domain weights
lifestyle (book_gym_session): { time: 0.30, taste: 0.30, budget: 0.30, safety: 0.10 }
TIME
SIGNALS USED:
- distance_from_user_km weight 0.30
- operating_hours.is_open_now HARD FILTER
- gym.is_24x7=true if late-night slot weight 0.10
- current_capacity.capacity_pct (lower=better) weight 0.30
- max_wait_minutes_for_equipment match weight 0.20
- peak_hours_today avoidance weight 0.10
TASTE
SIGNALS USED:
- gym.ratings.equipment_quality_score weight 0.20
- gym.ratings.crowd_density_score weight 0.20
- gym.ratings.trainer_helpfulness_score weight 0.15
- equipment_and_classes_catalog match workout_intent weight 0.20
- amenities match preferences weight 0.15
- trainer_floor_walker_included if requested weight 0.10
HARD FILTERS:
- shower_required AND showers_count=0
- locker_required AND lockers_count=0
- parking_required AND parking_available=false
- single_gender_floor_required AND no female_only/male_only floor
- workout_intent category not in equipment_and_classes_catalog
BUDGET
SIGNALS USED:
- session_pass.pass_inr vs band:
ok → budget_gym / community_gym
good → chain_gym / boutique_gym
great → premium_independent / celebrity_brand
- smoothie_voucher_included (bonus) weight 0.10
- shower_included (bonus) weight 0.10
- cancellation tier permissiveness weight 0.15
- online_payment_required match HARD FILTER
HARD FILTERS:
- session_pass.pass_inr > preferences.budget_max_inr_total → drop
SAFETY
SIGNALS USED:
- gym.trust.equipment_certified_safe HARD FILTER
- gym.trust.aed_defibrillator_present weight 0.30
- gym.trust.first_aid_kit_present HARD FILTER
- gym.trust.fire_safety_compliant HARD FILTER
- gym.trust.safety_audit_score weight 0.20
- gym.amenities.cctv_in_workout_zones weight 0.10
- gym.amenities.panic_button_at_floor weight 0.10
- trainer_floor_walker_required HARD FILTER (if true)
- medical_conditions disclosed AND physiotherapist_on_call weight 0.20
Hidden ranking factor
information_completeness_score weight 0.10.
7. COMPLETION CONTRACT
POST /api/v1/cpc/mcp_provider/{tomo_partner_id}
{
"intent": "lifestyle.book_gym_session",
"intent_version": "v1.0.0",
"external_id": "GYM-XYZ",
"amount_inr": 650,
"closed_at": "2026-05-10T20:14:00+05:30",
"request_id": "req_gym_8h2k_...",
"status": "completed",
"appointment_ref": "GYM-XYZ",
"merchant_id": "ChIJxxxxx",
"gym_id": "gym_8423",
"checked_in_at": "2026-05-10T18:32:00+05:30",
"checked_out_at": "2026-05-10T20:08:00+05:30",
"duration_minutes_actual": 96,
"categories_used": ["weight_training", "treadmill_cardio"],
"any_safety_incident": false,
"currency": "INR",
"fare_breakdown_total_inr": 650,
"rider_tip_inr": 0,
"ratings_pending": true,
"notes": ""
}
Status enum: completed | cancelled_by_user | cancelled_by_gym | no_show | failed | partial_completion_user_left_early
8. WIDGET (DELTA)
WIDGET TYPE: gym_session_options
SOURCE: src/widgets/types.ts
TYPE NAME: GymSessionOptionsPayload
RENDERED IN: components/widgets/GymSessionOptionsWidget.tsx
Default: 3 stacked rows with gym + capacity-pct badge + equipment-match strip + pass price. Tap → gym detail with equipment catalog + trainer roster + amenities → "Book session". Then GymSessionCard with QR check-in + locker assignment + AED location.
9. CACHING POLICY
| Call | TTL | Rationale |
|---|---|---|
search_gym_options |
60s | capacity shifts |
get_gym_detail |
5min | equipment catalog static-ish |
get_equipment_availability_now |
30s | live |
| Standard tools | per lifestyle.book_salon §9 |
inherited |
10. ERROR CODES (DELTA)
All from lifestyle.book_salon §10 REQUIRED. ADD:
| Code | HTTP | Meaning | TOMO behavior |
|---|---|---|---|
GYM_AT_CAPACITY |
503 | premises full | suggest later slot |
EQUIPMENT_OUT_OF_ORDER |
503 | requested equipment unavailable | surface alternates |
MEDICAL_DISCLOSURE_REQUIRED |
400 | undisclosed condition flagged | surface waiver flow |
PARTNER_INSURANCE_GAP |
503 | gym insurance lapsed | drop, fall back |
11. SANDBOX → PRODUCTION CHECKLIST
All from lifestyle.book_salon §11 REQUIRED. ADD:
[ ] equipment_certified_safe within last 12 months
[ ] AED defibrillator present + staff trained
[ ] first_aid_kit complete
[ ] insurance_coverage_inr verified
[ ] physiotherapist_on_call tested for medical-disclosure user
[ ] panic_button_at_floor functional
[ ] capacity_max accurate (TOMO field count)
[ ] trial_workout_buddy assigned for first-timer (if requested)
12. ANTI-FABRICATION RULES (DELTA)
All rules from lifestyle.book_salon §12 REQUIRED. ADD:
RULE 17 — Equipment safety claims real.
equipment_certified_safe=true with last cert > 12 months = breach.
RULE 18 — AED claim real.
aed_defibrillator_present=true requires functional, tested device.
Marketing prop without working AED = severe safety breach.
RULE 19 — No fake capacity_pct.
Inflating "low crowd" to attract users when actually full = breach.
RULE 20 — Trainer specializations real and active.
Crossfit L1 cert claim must be active CrossFit Inc registration.
RULE 21 — Class spots honest.
spots_remaining must decrement honestly. Showing "1 left!" when actually
empty = breach.
RULE 22 — Insurance current.
insurance_coverage_inr backed by IRDAI policy current at session date.
Lapsed insurance during session = severe breach.
RULE 23 — No silent class cancellation.
If class is cancelled within free_cancel window, partner refunds + offers
alternate. Pocketing = breach.
VERSION HISTORY
v1.0.0 — 2026-05-10 — Initial spec (delta over lifestyle.book_salon, gym extension)