T
TOMO
Developer Docs
BETA These docs are under partner review. Some features described are roadmap items, not yet shipped. Verify against your sandbox before relying on any contract.
● DRAFTv1.0.0lifestyle.book_gym_membership

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_class recurring 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)