lifestyle.book_gym_membership — Full Intent Specification
INTENT NAMESPACE: lifestyle
INTENT NAME: book_gym_membership
FULL ID: lifestyle.book_gym_membership
VERSION: v1.0.0
STATUS: draft
LAST UPDATED: 2026-05-10
TTBS WEIGHTS: time 0.20 · taste 0.30 · budget 0.40 · safety 0.10
Subscription-style gym/fitness/wellness club membership: monthly, quarterly, half-yearly, annual. Inherits from lifestyle.book_gym_session for the gym shape AND from food.subscribe_tiffin / mobility.book_recurring_commute for the subscription billing shape. Locked structural fields: (a) tenure_kind enum (1-month, 3-month, 6-month, 12-month, lifetime, single_session_pack); (b) auto_renew, pause_max_days, freeze_kind (medical, travel, etc.); (c) billing_period aggregates per-period billing; (d) included_amenities enum granular (basic, full, premium); (e) per-period CPC + closure CPC.
Inheritance contract: every gym-side field from lifestyle.book_gym_session and every subscription-side field from food.subscribe_tiffin/mobility.book_recurring_commute is REQUIRED.
1. NATURAL LANGUAGE COVERAGE
Classifies IN
- "annual gym membership"
- "Cult.fit yearly membership"
- "1-month gym pass"
- "monthly gym fees"
- "yearly fitness club membership"
- "premium gym membership with classes"
- "couples gym membership"
- "lifetime gym membership"
- "gym membership for office team"
Classifies OUT — borderline NO
- "drop-in gym session" →
lifestyle.book_gym_session - "personal trainer" →
lifestyle.book_personal_trainer - "yoga monthly" → potentially
lifestyle.book_yoga_classrecurring or partner-specific package - "wellness retreat" →
lifestyle.book_wellness_retreat
MULTI-INTENT TRIGGERS
- "annual gym membership and PT pack" →
lifestyle.book_gym_membership+lifestyle.book_personal_trainer - "gym membership and protein subscription" →
lifestyle.book_gym_membership+grocery.order_delivery
2. INPUT — TOMO → PROVIDER
{
"intent": "lifestyle.book_gym_membership",
"intent_version": "v1.0.0",
"request_id": "req_gymm_8h2k_2026-05-10T08:00:00Z",
"user_session_id": "anon_user_token_or_uid",
"tenure_kind": "annual",
"search_area": { ... }, // same as gym_session
"user_fitness_profile": { ... }, // same as gym_session
"membership_intent": {
"active_from_iso": "2026-06-01",
"active_until_iso": "2027-05-31",
"billing_period_kind": "monthly", // monthly | quarterly | annual_paid_monthly | annual_lump_sum
"auto_renew": true,
"pause_max_days_per_period": 30,
"transferability": "non_transferable",
"couples_or_family_kind": "individual", // individual | couples | family_2_4
"include_personal_trainer_sessions": false,
"personal_trainer_sessions_count": 0,
"include_classes_unlimited": true,
"include_pool_access": true,
"include_sauna_steam": true,
"include_spa_credits_inr": 0,
"include_smoothie_voucher_count_per_month": 4,
"include_nutrition_consultation": false
},
"preferences": {
"gym_kind_acceptable": ["chain_gym", "boutique_gym"],
"gym_kind_preferred": "chain_gym",
"shower_required": true,
"locker_required": true,
"ac_required": true,
"parking_required": true,
"single_gender_floor_required": false,
"single_gender_floor_hours_required": false,
"trainer_floor_walker_required": true,
"membership_kind_preferred": "premium",
"budget_band": "good",
"budget_max_inr_per_period": 4500,
"online_payment_required": true,
"trial_first_session_optional": true
},
"context": { ... }
}
| Field | Type | Constraint | Notes |
|---|---|---|---|
intent |
string | REQUIRED, equals "lifestyle.book_gym_membership" |
|
tenure_kind |
enum | REQUIRED, see §5 | |
membership_intent.active_from_iso |
ISO_DATE | REQUIRED | |
membership_intent.active_until_iso |
ISO_DATE | REQUIRED | |
membership_intent.billing_period_kind |
enum | REQUIRED, see §5 | |
membership_intent.auto_renew |
bool | REQUIRED | |
membership_intent.pause_max_days_per_period |
int | REQUIRED, ≥0 | |
membership_intent.transferability |
enum | REQUIRED, STRICT non_transferable | transferable_within_household | fully_transferable |
|
membership_intent.couples_or_family_kind |
enum | REQUIRED, see §5 | |
membership_intent.include_personal_trainer_sessions |
bool | REQUIRED | |
membership_intent.personal_trainer_sessions_count |
int | REQUIRED, ≥0 | 0 if not included |
membership_intent.include_classes_unlimited |
bool | REQUIRED | |
membership_intent.include_pool_access |
bool | REQUIRED | |
membership_intent.include_sauna_steam |
bool | REQUIRED | |
membership_intent.include_spa_credits_inr |
INR_INTEGER | REQUIRED | 0 if not |
membership_intent.include_smoothie_voucher_count_per_month |
int | REQUIRED, ≥0 | |
membership_intent.include_nutrition_consultation |
bool | REQUIRED | |
preferences.membership_kind_preferred |
enum | REQUIRED, see §5 | |
preferences.budget_max_inr_per_period |
INR_INTEGER | REQUIRED |
Anti-fabrication preamble (universal): no paid placement, no urgency text, no commission-influenced fields.
3. PROVIDER TOOLS
10 tools: search, detail, slots (for gym tour), book, get_state, pause, resume, freeze, modify, cancel. ADD:
Tool 11: get_membership_perks_summary
PURPOSE: live perks usage summary
INPUT: { membership_ref, request_id }
OUTPUT: { perks_used: PerksSummary, period_inr_charged_so_far, remaining_period_inr }
SLA: p95 < 600ms
Tool 12: transfer_membership
PURPOSE: transfer to another user (if transferability allows)
INPUT: { membership_ref, transfer_to_user_phone, request_id }
OUTPUT: { status, transfer_iso, fee_inr }
SLA: p95 < 1500ms
All 12 REQUIRED.
4. RESPONSE SHAPE
GymMembershipOption
All gym-side fields from lifestyle.book_gym_session §4 REQUIRED. ADD:
membership_kind_offered: STRICT ENUM, REQUIRED # see §5
tenure_kind_offered: STRICT ENUM, REQUIRED
membership_perks:
basic_floor_access: boolean, REQUIRED
premium_floor_access: boolean, REQUIRED
classes_unlimited_access: boolean, REQUIRED
classes_count_per_month_max: int, REQUIRED # 999 = unlimited
pool_access: boolean, REQUIRED
sauna_steam_access: boolean, REQUIRED
trainer_floor_walker_assigned: boolean, REQUIRED
pt_sessions_per_period: int, REQUIRED, ≥0
pt_session_value_per_session_inr: INR_INTEGER, REQUIRED # 0 if not included
spa_credits_per_period_inr: INR_INTEGER, REQUIRED
smoothie_voucher_per_month: int, REQUIRED, ≥0
nutrition_consult_per_period: int, REQUIRED, ≥0
guest_passes_per_period: int, REQUIRED, ≥0
branch_transfer_supported: boolean, REQUIRED
multi_branch_count_accessible: int, REQUIRED, ≥1 # 1 = same branch only
freeze_max_days_per_period: int, REQUIRED, ≥0
freeze_kinds_supported: array<STRICT ENUM>, REQUIRED, may be empty
cancellation_policy_kind: STRICT ENUM, REQUIRED # see §5
fare:
total_period_inr: INR_INTEGER, REQUIRED
per_month_avg_inr: INR_INTEGER, REQUIRED
joining_fee_one_time_inr: INR_INTEGER, REQUIRED
registration_fee_inr: INR_INTEGER, REQUIRED
emi_supported: boolean, REQUIRED
emi_options: array, REQUIRED, may be empty
- tenure_months: int, REQUIRED
monthly_emi_inr: INR_INTEGER, REQUIRED
no_cost_emi: boolean, REQUIRED
processing_fee_inr: INR_INTEGER, REQUIRED
full_payment_discount_pct: float, REQUIRED, 0-1 # for annual lump-sum
fare_breakdown_text: string, REQUIRED
is_upfront_fare: boolean, REQUIRED
fare_locked_until_iso: ISO_DATETIME, REQUIRED
billing:
period_kind: STRICT ENUM, REQUIRED
charge_iso: ISO_DATETIME, REQUIRED
payment_method_required: STRICT ENUM, REQUIRED
prorated_first_period: boolean, REQUIRED
proration_method: STRICT ENUM, REQUIRED
refund_policy: STRICT ENUM, REQUIRED
late_payment_charge_inr: INR_INTEGER, REQUIRED
cancellation:
free_cancel_within_first_n_days: int, REQUIRED, ≥0
cancel_charge_inr_per_remaining_period: INR_INTEGER, REQUIRED
exit_charge_inr: INR_INTEGER, REQUIRED
refund_processing_days: int, REQUIRED, ≥0
pro_rata_refund_supported: boolean, REQUIRED
# (all other gym-side fields from lifestyle.book_gym_session - REQUIRED)
GymMembershipState
membership_ref: string, REQUIRED
status: STRICT ENUM, REQUIRED
status_updated_iso: ISO_DATETIME, REQUIRED
status_history: array, REQUIRED, ≥1
period_summary:
current_period_start_iso: ISO_DATETIME, REQUIRED
current_period_end_iso: ISO_DATETIME, REQUIRED
current_period_inr_charged: INR_INTEGER, REQUIRED, ≥0
next_period_inr: INR_INTEGER, REQUIRED
visits_this_period: int, REQUIRED, ≥0
classes_attended_this_period: int, REQUIRED, ≥0
pt_sessions_used_this_period: int, REQUIRED, ≥0
smoothie_vouchers_used_this_period: int, REQUIRED, ≥0
guest_passes_used_this_period: int, REQUIRED, ≥0
spa_credits_used_inr: INR_INTEGER, REQUIRED, ≥0
nutrition_consult_used: int, REQUIRED, ≥0
freeze_state:
is_frozen: boolean, REQUIRED
freeze_kind: STRICT ENUM, REQUIRED # if frozen
frozen_from_iso: ISO_DATETIME, REQUIRED # may equal future
frozen_until_iso: ISO_DATETIME, REQUIRED
freeze_days_used_this_period: int, REQUIRED, ≥0
freeze_days_remaining: int, REQUIRED, ≥0
billing_state:
next_charge_iso: ISO_DATETIME, REQUIRED
payment_method_status: STRICT ENUM, REQUIRED
auto_renew_active: boolean, REQUIRED
past_due_amount_inr: INR_INTEGER, REQUIRED, ≥0
support_phone: string, REQUIRED
support_email: string, REQUIRED
Forbidden fields
paid_placement_score | sponsored_rank | promotion_priority |
fake_total_active_members | auto_inflate_visits |
ai_generated_photos (must equal false) | hidden_membership_fee |
hidden_late_payment_charge | undisclosed_freeze_charge |
fake_emi_no_cost | fake_full_payment_discount_pct
5. CONTROLLED VOCABULARIES
All gym-side vocabularies from lifestyle.book_gym_session §5 REQUIRED.
tenure_kind
1_month | 3_months | 6_months | 9_months | 12_months |
24_months | lifetime | single_session_pack
membership_intent.billing_period_kind
monthly | quarterly | annual_paid_monthly | annual_lump_sum |
quarterly_lump_sum | one_time
membership_intent.couples_or_family_kind
individual | couples | family_2_4 | family_5_plus | corporate_team
preferences.membership_kind_preferred and membership_kind_offered
basic | standard | premium | luxury_with_spa | corporate |
women_only | seniors | youth | family
membership_perks.freeze_kinds_supported[]
medical_emergency | travel | pregnancy | maternity_leave |
vacation_planned | other | none
membership_perks.cancellation_policy_kind
free_within_grace_only | pro_rata_anytime | flat_fee_anytime |
no_cancellation_post_signup | refund_only_in_first_n_days
billing.period_kind
monthly | quarterly | annual_paid_monthly | annual_lump_sum
billing.payment_method_required
upi_autopay | card_recurring_mandate | netbanking_emandate |
wallet_auto_recharge | one_time_full_payment
billing.proration_method
exact_per_visit | flat_first_period | no_proration | pro_rata_remaining
billing.refund_policy
unused_pro_rata | flat_50pct_unused | flat_70pct_unused | no_refund_after_period_starts
GymMembershipState.status
pending_first_visit | active | paused_by_user | frozen_medical |
frozen_travel | billing_failed | renewal_failed | cancelled_by_user |
cancelled_by_partner | expired | failed | transferred
GymMembershipState.freeze_state.freeze_kind
not_frozen | medical_emergency | travel | pregnancy |
maternity_leave | vacation_planned | other
GymMembershipState.billing_state.payment_method_status
active | pending_authorization | failed_last_charge | mandate_revoked
cancel_subscription.reason
user_changed_address | user_dissatisfied | medical | financial |
moved_city | found_alternative | other
pause_subscription.reason and freeze_kind
medical_emergency | travel | pregnancy | maternity_leave |
vacation_planned | other
merchant_id resolution order
1. Google Place ID
2. Municipal license number
3. partner_id + ":" + gym_id
6. TTBS DIMENSIONS
Per-domain weights
lifestyle (book_gym_membership): { time: 0.20, taste: 0.30, budget: 0.40, safety: 0.10 }
Budget rises (long-term subscription, financial commitment heavier).
TIME
SIGNALS USED:
- distance_from_user_km weight 0.40
- operating_hours.is_24x7 (matches user's late-night patterns) weight 0.20
- branch_transfer_supported (multi-branch flexibility) weight 0.20
- cancellation tier permissiveness weight 0.20
TASTE
SIGNALS USED:
- gym.ratings.equipment_quality_score weight 0.20
- gym.ratings.recent_30day_score weight 0.20
- membership_perks match preferences (PT, classes, pool, spa) weight 0.30
- gym amenities match preferences weight 0.20
- guest_passes_per_period (more=better) weight 0.10
HARD FILTERS:
- membership_kind_preferred specifics (premium needs premium-floor)
- couples_or_family_kind=couples requires partner-supported
BUDGET
SIGNALS USED:
- fare.total_period_inr vs band:
ok → budget_gym / community_gym
good → chain_gym / boutique_gym
great → luxury_with_spa / celebrity_brand
- per_month_avg_inr (lower=better all else equal) weight 0.30
- joining_fee_one_time_inr (lower=better) weight 0.20
- emi_supported AND no_cost_emi available weight 0.20
- full_payment_discount_pct (higher=better for lump sum) weight 0.10
- cancellation tier permissiveness weight 0.10
- exit_charge_inr (lower=better) weight 0.10
HARD FILTERS:
- fare.total_period_inr > preferences.budget_max_inr_per_period × tenure_months → drop
- online_payment_required AND payment_method=cash_only → drop
SAFETY
(Same gym-safety signals from lifestyle.book_gym_session §6 — fire safety, AED, equipment cert. HARD FILTERS preserved.)
Hidden ranking factor
information_completeness_score weight 0.10.
7. COMPLETION CONTRACT
Like other subscriptions, two events.
Per-period settlement
POST /api/v1/cpc/mcp_provider/{tomo_partner_id}
{
"intent": "lifestyle.book_gym_membership",
"intent_version": "v1.0.0",
"event_kind": "period_settled",
"external_id": "GYMM-XYZ",
"membership_ref": "GYMM-XYZ",
"amount_inr": 3500,
"closed_at": "2026-07-01T00:01:00+05:30",
"request_id": "req_gymm_8h2k_...",
"status": "period_completed",
"period_start_iso": "2026-06-01T00:00:00+05:30",
"period_end_iso": "2026-06-30T23:59:59+05:30",
"visits_in_period": 18,
"classes_attended": 6,
"pt_sessions_used": 0,
"freeze_days_used": 0,
"perks_consumed_inr": 0,
"currency": "INR"
}
Membership closure
POST /api/v1/cpc/mcp_provider/{tomo_partner_id}
{
"intent": "lifestyle.book_gym_membership",
"intent_version": "v1.0.0",
"event_kind": "membership_closed",
"external_id": "GYMM-XYZ",
"membership_ref": "GYMM-XYZ",
"amount_inr": 42000,
"closed_at": "2027-05-31T23:59:59+05:30",
"status": "completed",
"membership_started_at": "2026-06-01T00:00:00+05:30",
"membership_ended_at": "2027-05-31T23:59:59+05:30",
"total_periods_billed": 12,
"total_visits": 189,
"total_classes_attended": 67,
"total_freeze_days": 21,
"currency": "INR",
"ratings_pending": true
}
Status enums: as mobility.book_recurring_commute §7.
8. WIDGET / 9. CACHING / 10. ERROR CODES / 11. CHECKLIST
(Inherit from lifestyle.book_gym_session and food.subscribe_tiffin. Widget type: gym_membership_options.)
12. ANTI-FABRICATION RULES
All from lifestyle.book_gym_session + subscription rules from food.subscribe_tiffin REQUIRED. ADD:
RULE 17 — No fake EMI no-cost.
no_cost_emi=true must reflect actual zero-interest pass-through. Bank
charging interest while marketed no-cost = consumer-protection breach.
RULE 18 — Joining fee transparent.
joining_fee_one_time_inr disclosed up-front. Pop-up fees at signup = breach.
RULE 19 — Pro-rata refund honored.
pro_rata_refund_supported=true must produce calculable refund = visits-remaining
× per-visit-inr. Inflating "deductions" to retain money = breach.
RULE 20 — Freeze days honored.
freeze_max_days_per_period must be enforceable. Refusing freeze for valid
freeze_kind = breach.
RULE 21 — Auto-renew notification 7 days before charge.
RULE 22 — Mandate revocation respected.
RULE 23 — Class spots not silently blocked.
classes_count_per_month_max claim must be deliverable. Selectively blocking
premium classes = breach.
RULE 24 — Branch transfer real.
branch_transfer_supported=true must allow user to use any branch listed.
Blocking specific branches without disclosure = breach.
RULE 25 — Lifetime membership real.
lifetime tenure must allow access for actual lifetime (not "until our terms
change"). Voiding lifetime memberships unilaterally = severe breach.
RULE 26 — Couples/family transferability honest.
couples_or_family_kind=couples means BOTH users have access. Restricting
one user = breach.
VERSION HISTORY
v1.0.0 — 2026-05-10 — Initial spec (delta over gym_session + subscription pattern)