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.0mobility.book_intercity_ride

mobility.book_intercity_ride — Full Intent Specification

INTENT NAMESPACE: mobility
INTENT NAME:      book_intercity_ride
FULL ID:          mobility.book_intercity_ride
VERSION:          v1.0.0
STATUS:           draft
LAST UPDATED:     2026-05-10
TTBS WEIGHTS:     time 0.30 · taste 0.10 · budget 0.30 · safety 0.30

Intercity rides differ from intracity in four locked ways: (a) always scheduled, never ASAP; (b) multi-toll FASTag arithmetic is load-bearing, not nice-to-have; (c) driver allowance + night halt + state-border charges expand the fare breakdown; (d) safety weight rises (highway, longer exposure, fatigue risk).


1. NATURAL LANGUAGE COVERAGE

Classifies IN

  • "Hyderabad to Bangalore cab tomorrow morning"
  • "book a cab to Vijayawada on Friday"
  • "intercity ride Mumbai to Pune Saturday 6am"
  • "one-way taxi to Tirupati Sunday"
  • "Hyderabad to Warangal Innova for 4 people"
  • "round trip cab to Goa next weekend"
  • "Hyderabad to Chennai outstation tomorrow"
  • "drop me to Bengaluru by Sunday evening"
  • "we're 6 people, intercity Tempo Traveller to Coorg"

Classifies OUT — borderline NO

  • "ride to airport" → mobility.book_intracity_ride (or mobility.book_airport_transfer)
  • "self-drive Innova for the weekend Hyderabad to Goa" → mobility.book_self_drive
  • "Goa 4N5D package with cab" → mobility.book_outstation_package
  • "chauffeur for full day in Bangalore" → mobility.book_chauffeur_hourly
  • "auto to MG Road" → mobility.book_intracity_ride
  • "I want to drive to Goa myself" → no intent (informational)

MULTI-INTENT TRIGGERS

  • "Hyderabad to Bangalore cab Friday morning, also book hotel near MG Road" → mobility.book_intercity_ride + travel.book_hotel
  • "intercity to Tirupati and dinner reservation on the way at Tirupati Bhojanam" → mobility.book_intercity_ride + food.book_dine_in
  • "cab to Vijayawada round trip and recharge FASTag" → mobility.book_intercity_ride + pay.fastag_topup

2. INPUT — TOMO → PROVIDER

{
  "intent":          "mobility.book_intercity_ride",
  "intent_version":  "v1.0.0",
  "request_id":      "req_intc_8h2k_2026-05-10T06:14:00Z",
  "user_session_id": "anon_user_token_or_uid",

  "pickup": {
    "lat":            17.4435,
    "lng":            78.3772,
    "address":        "HITEC City Main Road, Madhapur",
    "neighborhood":   "Madhapur",
    "city":           "Hyderabad",
    "pincode":        "500081",
    "state_code":     "TS",
    "country_code":   "IN",
    "place_kind":     "office",
    "instructions":   "Pickup gate B"
  },

  "drop": {
    "lat":            12.9716,
    "lng":            77.5946,
    "address":        "MG Road Metro, Bangalore",
    "neighborhood":   "MG Road",
    "city":           "Bangalore",
    "pincode":        "560001",
    "state_code":     "KA",
    "country_code":   "IN",
    "place_kind":     "hotel",
    "instructions":   "Lobby of Taj MG Road"
  },

  "intermediate_stops": [
    {
      "lat":            16.5062,
      "lng":            80.6480,
      "address":        "Vijayawada food court NH-65",
      "city":           "Vijayawada",
      "stop_kind":      "food_break",
      "expected_minutes": 30
    }
  ],

  "trip_kind":            "one_way",
  "scheduled_pickup_iso": "2026-05-11T06:00:00+05:30",
  "scheduled_return_iso": null,
  "expected_return_after_days": 0,

  "party": {
    "passenger_count":  4,
    "luggage_pieces":   6,
    "luggage_size":     "checkin",
    "minor_count":      1,
    "senior_count":     0
  },

  "preferences": {
    "vehicle_kinds_acceptable":   ["sedan_intercity", "suv_intercity", "tempo_traveller"],
    "budget_band":                "good",
    "budget_max_inr":              14500,
    "ac_required":                 true,
    "female_driver_required":      false,
    "ev_only":                     false,
    "wheelchair_accessible_required": false,
    "child_seat_required":         true,
    "max_seat_capacity_min":       4,
    "driver_must_speak_locales":   ["en-IN", "te-IN", "kn-IN"],
    "no_pool":                     true
  },

  "route_context": {
    "estimated_distance_km":     570.0,
    "estimated_duration_min":    540,
    "involves_toll":             true,
    "toll_count":                7,
    "states_crossed":            ["TS", "AP", "KA"],
    "state_border_count":        2,
    "involves_highway":          true,
    "primary_highway_codes":     ["NH-65", "NH-44"],
    "weather_celsius":           29,
    "rain_probability_pct":      30,
    "current_aqi":               135,
    "is_overnight":              false,
    "is_late_night_departure":   false,
    "is_late_night_arrival":     false,
    "is_surge_window":           false
  },

  "context": {
    "user_locale":          "en-IN",
    "user_currency_pref":   "INR",
    "trip_purpose":         "leisure",
    "trust_signals": {
      "is_repeat_customer":          true,
      "prior_intercity_rides_with_partner": 4,
      "user_account_age_days":       312,
      "fastag_present":              true,
      "fastag_balance_inr":          1840
    }
  }
}
Field Type Constraint Notes
intent string REQUIRED, equals "mobility.book_intercity_ride"
pickup.state_code string REQUIRED mandatory for intercity (different from intracity)
drop.state_code string REQUIRED
intermediate_stops array REQUIRED, may be empty each stop has full address + stop_kind
intermediate_stops[].stop_kind enum REQUIRED, see §5
trip_kind enum REQUIRED, STRICT one_way | round_trip
scheduled_pickup_iso ISO_DATETIME REQUIRED no ASAP for intercity
scheduled_return_iso ISO_DATETIME or null REQUIRED, null only if trip_kind=one_way
expected_return_after_days int REQUIRED, ≥0 drives night halt charge math
party.minor_count int REQUIRED, ≥0 child seat + safety weight
party.senior_count int REQUIRED, ≥0 comfort + medical-assist signals
route_context.states_crossed array REQUIRED, ≥1 ISO 3166-2:IN state codes
route_context.state_border_count int REQUIRED, ≥0 drives state-border charge
route_context.primary_highway_codes array REQUIRED, may be empty for permit + safety routing
route_context.is_overnight bool REQUIRED true if pickup→drop crosses 23:00–05:00
route_context.is_late_night_departure bool REQUIRED pickup_local_time in [22:30, 05:30)
route_context.is_late_night_arrival bool REQUIRED arrival_local_time in [22:30, 05:30)
context.trust_signals.fastag_balance_inr INR_INTEGER REQUIRED, ≥0 TOMO computes sufficiency vs estimated_toll_total_inr

Anti-fabrication preamble (universal): no paid placement, no urgency text, no commission-influenced fields.


3. PROVIDER TOOLS

Tool 1: get_intercity_estimates

PURPOSE:        return intercity ride options across vehicle kinds
INPUT:          §2 request body
OUTPUT:         { options: IntercityRideOption[], result_token, expires_at }
SLA:            p50 < 600ms, p95 < 1500ms
RATE LIMIT:     ≤ 1/sec per user

Tool 2: book_intercity_ride

PURPOSE:        commit a ride
INPUT:          { ride_option_id, payment_token, request_id, idempotency_key, user_phone, otp_required }
OUTPUT:         { ride_ref, status, driver, vehicle, fare_quote, otp_for_driver, allowance_breakdown }
SLA:            p95 < 5000ms
IDEMPOTENCY:    REQUIRED on idempotency_key

Tool 3: track_intercity_ride

PURPOSE:        live ride state + driver location + leg progress
INPUT:          { ride_ref, request_id }
OUTPUT:         IntercityRideTrack (§4)
SLA:            p95 < 500ms
RATE LIMIT:     ≤ 1 every 10s

Tool 4: cancel_intercity_ride

PURPOSE:        cancel
INPUT:          { ride_ref, reason, request_id }
OUTPUT:         { status, cancellation_charge_inr, refund_amount_inr, cancellation_window_kind }
SLA:            p95 < 2000ms

Tool 5: update_intercity_stops

PURPOSE:        add or modify intermediate stops mid-trip
INPUT:          { ride_ref, new_stops[], removed_stop_indices[], request_id }
OUTPUT:         { revised_fare_inr, revised_duration_min, status }
SLA:            p95 < 1500ms

Tool 6: rate_intercity_ride

PURPOSE:        post-ride rating
INPUT:          { ride_ref, rating_5star, comment, tip_inr, request_id }
OUTPUT:         { acknowledged: true }
SLA:            p95 < 800ms

Tool 7: share_trip_status

PURPOSE:        tokenized URL for live trip sharing
INPUT:          { ride_ref, recipient_phone_optional, request_id }
OUTPUT:         { share_url, expires_at }
SLA:            p95 < 500ms

All seven REQUIRED.


4. RESPONSE SHAPE

IntercityRideOption (returned by get_intercity_estimates)

id:                               string, REQUIRED
ride_option_token:                string, REQUIRED
expires_at:                       ISO_DATETIME, REQUIRED

vehicle_kind:                     STRICT ENUM, REQUIRED       # see §5
vehicle_class:                    STRICT ENUM, REQUIRED
display_label:                    string, REQUIRED            # "Innova Crysta Intercity"
seat_capacity:                    int, REQUIRED, ≥1
luggage_capacity:                 STRICT ENUM, REQUIRED

eta:
  pickup_minutes:                 int, REQUIRED                # driver-to-pickup window (large for intercity)
  trip_minutes:                   int, REQUIRED                # full intercity duration
  total_minutes:                  int, REQUIRED
  arrival_eta_iso:                ISO_DATETIME, REQUIRED       # pickup_iso + total_minutes
  driver_pre_dispatch_minutes:    int, REQUIRED                # how early driver is dispatched

fare:
  total_inr:                      INR_INTEGER, REQUIRED
  base_inr:                       INR_INTEGER, REQUIRED
  per_km_inr:                     float, REQUIRED
  total_km_charged:               float, REQUIRED              # often inflated for return-empty
  driver_allowance_inr:           INR_INTEGER, REQUIRED        # daily DA
  night_halt_charge_inr:          INR_INTEGER, REQUIRED        # if overnight
  night_halt_count:               int, REQUIRED, ≥0
  toll_inr:                       INR_INTEGER, REQUIRED
  toll_count:                     int, REQUIRED, ≥0
  state_border_charge_inr:        INR_INTEGER, REQUIRED        # 0 if same-state
  state_border_count:             int, REQUIRED, ≥0
  highway_charge_inr:             INR_INTEGER, REQUIRED        # 0 if not applicable
  ac_charge_inr:                  INR_INTEGER, REQUIRED        # 0 if AC default
  late_night_charge_inr:          INR_INTEGER, REQUIRED        # 0 if not late
  parking_charge_inr:             INR_INTEGER, REQUIRED
  platform_fee_inr:               INR_INTEGER, REQUIRED
  gst_inr:                        INR_INTEGER, REQUIRED
  rider_tip_optional_inr:         INR_INTEGER, REQUIRED        # default 0
  fare_breakdown_text:            string, REQUIRED
  is_upfront_fare:                boolean, REQUIRED
  fare_locked_until_iso:          ISO_DATETIME, REQUIRED
  return_trip_empty_pricing:      boolean, REQUIRED            # true if user pays for empty return
  per_km_overage_inr:             float, REQUIRED              # rate beyond included_km
  included_km:                    int, REQUIRED                # cap before overage starts

vehicle_amenities:
  ac:                             boolean, REQUIRED
  music_on_demand:                boolean, REQUIRED
  charging_port:                  boolean, REQUIRED
  wifi_in_cab:                    boolean, REQUIRED
  bottled_water:                  boolean, REQUIRED
  newspaper:                      boolean, REQUIRED
  pet_friendly:                   boolean, REQUIRED
  child_seat_available:           boolean, REQUIRED
  child_seat_kind:                STRICT ENUM, REQUIRED
  wheelchair_accessible:          boolean, REQUIRED
  recliner_seats:                 boolean, REQUIRED
  panoramic_roof:                 boolean, REQUIRED
  fridge_in_cabin:                boolean, REQUIRED            # luxury intercity only
  oxygen_supply_for_emergency:    boolean, REQUIRED

vehicle_meta:
  age_years:                      int, REQUIRED
  fuel_kind:                      STRICT ENUM, REQUIRED
  fuel_efficiency_kmpl:           float, REQUIRED
  ev_battery_charge_pct:          int, REQUIRED, 0-100
  ev_range_km_remaining:          int, REQUIRED                # 0 if not EV
  emission_norm:                  STRICT ENUM, REQUIRED
  registration_state_code:        string, REQUIRED
  vehicle_class_certification:    STRICT ENUM, REQUIRED
  comprehensive_insurance:        boolean, REQUIRED
  insurance_valid_until_iso:      ISO_DATE, REQUIRED
  fitness_certificate_valid_until_iso: ISO_DATE, REQUIRED
  puc_valid_until_iso:            ISO_DATE, REQUIRED
  permit_kind:                    STRICT ENUM, REQUIRED        # tourist | contract_carriage required for intercity
  all_india_tourist_permit:       boolean, REQUIRED
  permit_states_covered:          array<string>, REQUIRED, ≥1
  commercial_aggregator_permit_number: string, REQUIRED
  vehicle_color:                  string, REQUIRED
  last_serviced_iso:              ISO_DATE, REQUIRED
  odometer_reading_km:            int, REQUIRED, ≥0

driver_meta:
  driver_id:                      string, REQUIRED
  display_name:                   string, REQUIRED
  photo_url:                      URL, REQUIRED
  rating_avg:                     float, REQUIRED, 0-5
  rides_completed_total:          int, REQUIRED, ≥0
  intercity_rides_completed:      int, REQUIRED, ≥0
  partner_account_age_days:       int, REQUIRED, ≥0
  languages_spoken:               array<RFC_3066_LOCALE>, REQUIRED, ≥1
  female_driver:                  boolean, REQUIRED
  age_band:                       STRICT ENUM, REQUIRED
  experience_years_driving:       int, REQUIRED, ≥0
  highway_experience_years:       int, REQUIRED, ≥0
  states_familiar_with:           array<string>, REQUIRED, ≥1
  defensive_driving_certified:    boolean, REQUIRED

driver_kyc:
  dl_verified:                    boolean, REQUIRED
  dl_number_masked:               string, REQUIRED
  dl_valid_until_iso:             ISO_DATE, REQUIRED
  dl_class_includes_lmv:          boolean, REQUIRED
  rc_verified:                    boolean, REQUIRED
  aadhaar_verified:               boolean, REQUIRED
  pan_verified:                   boolean, REQUIRED
  background_check_passed:        boolean, REQUIRED
  background_check_iso:           ISO_DATETIME, REQUIRED
  badge_id_displayed:             boolean, REQUIRED
  fatigue_compliance_certified:   boolean, REQUIRED            # MV Act drive-hours cap

safety_features:
  sos_button_in_app:              boolean, REQUIRED
  trip_share_supported:           boolean, REQUIRED
  in_app_chat_supported:          boolean, REQUIRED
  in_app_call_supported:          boolean, REQUIRED
  emergency_contact_alerts:       boolean, REQUIRED
  cctv_in_cab:                    boolean, REQUIRED
  panic_alert_to_local_police:    boolean, REQUIRED
  driver_drowsiness_detection:    boolean, REQUIRED
  speed_limit_governing:          boolean, REQUIRED
  highway_assist_telematics:      boolean, REQUIRED
  spare_tyre_present:             boolean, REQUIRED
  first_aid_kit_present:          boolean, REQUIRED
  fire_extinguisher_present:      boolean, REQUIRED
  reflective_triangle_present:    boolean, REQUIRED

route_quality:
  estimated_distance_km:          float, REQUIRED
  estimated_duration_min:         int, REQUIRED
  highway_kms:                    float, REQUIRED
  inner_road_kms:                 float, REQUIRED
  states_crossed_count:           int, REQUIRED, ≥1
  primary_highway_codes:          array<string>, REQUIRED, may be empty
  toll_passes:                    array, REQUIRED, may be empty
    - toll_name:                  string, REQUIRED
      toll_amount_inr:            INR_INTEGER, REQUIRED
      fastag_supported:           boolean, REQUIRED
      cash_accepted:              boolean, REQUIRED
      state_code:                 string, REQUIRED

cancellation:
  free_cancel_until_iso:          ISO_DATETIME, REQUIRED       # absolute time, not relative window
  cancel_charge_after_grace_inr:  INR_INTEGER, REQUIRED
  cancel_charge_within_24h_inr:   INR_INTEGER, REQUIRED
  cancel_charge_within_2h_inr:    INR_INTEGER, REQUIRED
  no_show_charge_inr:             INR_INTEGER, REQUIRED
  driver_cancel_compensation_inr: INR_INTEGER, REQUIRED
  refund_processing_days:         int, REQUIRED, ≥0

fastag_check:
  toll_count_on_route:            int, REQUIRED, ≥0
  fastag_required:                boolean, REQUIRED
  estimated_toll_total_inr:       INR_INTEGER, REQUIRED
  user_fastag_balance_sufficient: boolean, REQUIRED
  fastag_topup_recommended_inr:   INR_INTEGER, REQUIRED        # 0 if not required

freshness:
  data_last_synced_iso:           ISO_DATETIME, REQUIRED

_provider:
  name:                           string, REQUIRED
  tomo_partner_id:                string, REQUIRED
  partner_tier:                   STRICT ENUM, REQUIRED
  deep_link:                      URL, REQUIRED
  customer_support_phone:         string, REQUIRED
  customer_support_24x7:          boolean, REQUIRED
  in_app_chat_supported:          boolean, REQUIRED
  partner_intercity_volume_30d:   int, REQUIRED, ≥0

IntercityRideTrack (returned by track_intercity_ride)

ride_ref:                         string, REQUIRED
status:                           STRICT ENUM, REQUIRED        # see §5
status_updated_iso:               ISO_DATETIME, REQUIRED
status_history:                   array, REQUIRED, ≥1
  - status:                       STRICT ENUM, REQUIRED
    iso:                          ISO_DATETIME, REQUIRED

leg_progress:
  current_leg_index:              int, REQUIRED, ≥0           # 0 = pickup→stop1, etc.
  total_legs:                     int, REQUIRED, ≥1
  current_leg_distance_remaining_km: float, REQUIRED
  next_stop_kind:                 STRICT ENUM, REQUIRED        # food_break | refuel | washroom | drop | none

driver:
  driver_id:                      string, REQUIRED
  display_name:                   string, REQUIRED
  phone_masked:                   string, REQUIRED
  photo_url:                      URL, REQUIRED
  rating_avg:                     float, REQUIRED, 0-5
  in_app_chat_supported:          boolean, REQUIRED
  in_app_call_supported:          boolean, REQUIRED
  cumulative_drive_minutes_today: int, REQUIRED, ≥0           # fatigue audit

vehicle:
  vehicle_kind:                   STRICT ENUM, REQUIRED
  display_label:                  string, REQUIRED
  color:                          string, REQUIRED
  plate_masked:                   string, REQUIRED

location:
  current_lat:                    float, REQUIRED
  current_lng:                    float, REQUIRED
  current_state_code:             string, REQUIRED
  heading_degrees:                float, REQUIRED, 0-360
  speed_kmh:                      float, REQUIRED, ≥0
  location_updated_iso:           ISO_DATETIME, REQUIRED
  status:                         STRICT ENUM, REQUIRED

eta:
  to_next_stop_minutes:           int, REQUIRED
  to_drop_minutes:                int, REQUIRED
  revised_fare_inr:               INR_INTEGER, REQUIRED
  detour_minutes:                 int, REQUIRED

ride_safety:
  on_route:                       boolean, REQUIRED
  unusual_stop_detected:          boolean, REQUIRED
  unusual_speed_detected:         boolean, REQUIRED
  driver_fatigue_warning_active:  boolean, REQUIRED
  trip_share_active:              boolean, REQUIRED
  trip_share_url:                 URL, REQUIRED

fastag:
  next_toll_distance_km:          float, REQUIRED              # 9999 if none
  next_toll_amount_inr:           INR_INTEGER, REQUIRED
  user_fastag_balance_sufficient_for_remaining_tolls: boolean, REQUIRED

support_phone:                    string, REQUIRED
support_email:                    string, REQUIRED

Forbidden fields

paid_placement_score | sponsored_rank | promotion_priority |
artificial_demand_text | fake_recent_booking_text |
auto_inflate_driver_rating | partner_paid_for_top_listing |
fake_intercity_volume_30d | inflated_highway_experience_years

5. CONTROLLED VOCABULARIES

vehicle_kind

sedan_intercity | suv_intercity | premium_sedan_intercity |
premium_suv_intercity | tempo_traveller | mini_bus |
ev_sedan_intercity | ev_suv_intercity | luxury_sedan_intercity | luxury_suv_intercity

vehicle_class

economy | comfort | premium | luxury | xl | xl_premium

IntercityRideOption.luggage_capacity

small | medium | large | xl | xxl

pickup.place_kind and drop.place_kind

home | office | airport | railway | metro_station | bus_terminal |
hotel | restaurant | mall | hospital | clinic | school | college |
event_venue | religious_place | landmark | other

intermediate_stops[].stop_kind

food_break | washroom | refuel | charge_ev | religious_visit |
sightseeing | medical | luggage_pickup | passenger_pickup | other

trip_kind

one_way | round_trip

party.luggage_size

none | handbag | backpack | cabin | checkin | oversized

route_context.is_overnight

boolean. Definition: any portion of trip falls between 23:00 and 05:00 local.

IntercityRideOption.vehicle_meta.fuel_kind

petrol | diesel | cng | lpg | ev_full | hybrid | bs6_petrol | bs6_diesel

IntercityRideOption.vehicle_meta.emission_norm

bs3 | bs4 | bs6 | ev | unknown_legacy

IntercityRideOption.vehicle_meta.permit_kind

tourist | contract_carriage | aggregator_permit | all_india_tourist

Intercity REQUIRES one of tourist | all_india_tourist | contract_carriage. Aggregator-only permits are NOT valid for cross-state trips.

IntercityRideOption.vehicle_meta.vehicle_class_certification

commercial_yellow_plate | tourist | tempo_traveller | luxury_charter

vehicle_amenities.child_seat_kind

none | infant | toddler | booster | universal

driver_meta.age_band

21-30 | 31-40 | 41-55 | 56+

Note: minimum 21 for intercity (commercial highway).

IntercityRideTrack.status

scheduled | driver_assigned | driver_dispatched | driver_arriving |
arrived_at_pickup | otp_required | trip_started | en_route_leg_1 |
at_intermediate_stop | en_route_leg_n | nearby_drop |
completed | cancelled_by_user | cancelled_by_driver |
delayed_by_driver | delayed_by_route | failed

IntercityRideTrack.location.status

heading_to_pickup | at_pickup | trip_in_progress | at_food_stop |
at_refuel_stop | at_charge_stop | crossing_state_border |
nearby_drop | reached_drop

leg_progress.next_stop_kind

food_break | refuel | washroom | charge_ev | religious_visit |
sightseeing | medical | drop | none

cancel_intercity_ride.reason

user_changed_mind | wrong_vehicle | wrong_address | driver_too_far |
driver_unresponsive | safety_concern | weather_cancellation |
no_longer_needed | found_alternative | emergency

context.trip_purpose

commute_to_work | commute_to_home | leisure | family_visit |
medical_visit | religious_pilgrimage | wedding_event | business_trip |
funeral | emergency | other

6. TTBS DIMENSIONS

Per-domain weights (locked; intercity override on safety)

mobility (intercity): { time: 0.30, taste: 0.10, budget: 0.30, safety: 0.30 }

Safety up from 0.20 (intracity) to 0.30 — highway exposure, fatigue risk, longer trip.

TIME

SIGNALS USED:
  - eta.pickup_minutes                              weight 0.20
  - eta.driver_pre_dispatch_minutes (lower=better)  weight 0.10
  - route_quality.estimated_duration_min            weight 0.40
  - route_quality.highway_kms / total_km            weight 0.20  (more highway = faster)
  - cancellation.refund_processing_days (low=trust) weight 0.10

USER BAND HANDLING:
  - "by 6pm Sunday" hard arrival → arrival_eta_iso ≤ deadline → HARD FILTER
  - "earliest possible" → upweight pickup_minutes 0.40

TASTE

SIGNALS USED:
  - driver_meta.rating_avg                          weight 0.30
  - vehicle_meta.age_years (newer = better)         weight 0.15
  - vehicle_amenities match w/ user prefs           weight 0.30
  - driver languages match user_locale + states     weight 0.15
  - vehicle_amenities.recliner_seats (long trip)    weight 0.10

HARD FILTER:
  - ac_required + ac=false → drop

BUDGET

SIGNALS USED:
  - fare.total_inr vs band:
      ok    → sedan_intercity / EV economy
      good  → suv_intercity / sedan_intercity_premium
      great → luxury_sedan_intercity / luxury_suv_intercity
  - fare.return_trip_empty_pricing=false (better)    weight 0.20
  - fare.is_upfront_fare=true (locked = better)      weight 0.20
  - fastag_topup_recommended_inr (lower or zero)     weight 0.10

HARD FILTERS:
  - fare.total_inr > preferences.budget_max_inr → drop

USER BAND HANDLING:
  - emergency trip_purpose → budget weight 0.50 of stated → safety upweighted
  - leisure trip → budget weight unchanged

SAFETY

SIGNALS USED:
  - driver_kyc.background_check_passed              HARD FILTER
  - driver_kyc.dl_class_includes_lmv                HARD FILTER
  - driver_kyc.fatigue_compliance_certified         HARD FILTER
  - vehicle_meta.comprehensive_insurance            HARD FILTER
  - vehicle_meta.permit_kind in (tourist|all_india_tourist|contract_carriage) HARD FILTER
  - vehicle_meta.permit_states_covered ⊇ states_crossed HARD FILTER
  - safety_features.sos_button_in_app               weight 0.10
  - safety_features.driver_drowsiness_detection     weight 0.15
  - safety_features.highway_assist_telematics       weight 0.15
  - safety_features.first_aid_kit_present           weight 0.05
  - safety_features.fire_extinguisher_present       weight 0.05
  - safety_features.reflective_triangle_present     weight 0.05
  - driver_meta.intercity_rides_completed (≥20)     weight 0.15
  - driver_meta.highway_experience_years (≥3)       weight 0.10
  - driver_meta.defensive_driving_certified         weight 0.10
  - driver_meta.female_driver
      (boosted if female_driver_required)           weight 0.10
  - is_overnight → safety weight scales 1.6x
  - is_late_night_arrival → trip_share enforced

HARD FILTERS:
  - female_driver_required → drop male drivers
  - child_seat_required AND minor_count > 0 → drop without seat
  - ev_only → drop non-EV
  - states_crossed not covered by permit → drop

Hidden ranking factor

information_completeness_score weight 0.10.

FASTag check

If route_context.involves_toll AND user_fastag_balance_sufficient = false, TOMO surfaces a top-up CTA before booking. For intercity, also surface FASTag balance vs estimated_toll_total_inr at the option-card level (not just confirmation).


7. COMPLETION CONTRACT

POST /api/v1/cpc/mcp_provider/{tomo_partner_id}
X-TOMO-Timestamp: <ms>
X-TOMO-Signature: sha256=<hex>

{
  "intent":            "mobility.book_intercity_ride",
  "intent_version":    "v1.0.0",
  "external_id":       "OLA-INTC-XYZ",
  "amount_inr":         13800,
  "closed_at":         "2026-05-11T15:34:00+05:30",
  "request_id":        "req_intc_8h2k_...",
  "status":            "completed",
  "ride_ref":          "OLA-INTC-XYZ",
  "started_at":        "2026-05-11T06:08:00+05:30",
  "completed_at":      "2026-05-11T15:34:00+05:30",
  "distance_traveled_km": 583.2,
  "duration_minutes":   566,
  "promised_arrival_iso": "2026-05-11T15:00:00+05:30",
  "actual_arrival_iso":   "2026-05-11T15:34:00+05:30",
  "stops_completed":      1,
  "states_traversed":     ["TS", "AP", "KA"],
  "tolls_paid_inr":       1245,
  "currency":          "INR",
  "fare_breakdown_total_inr": 13800,
  "rider_tip_inr":       0,
  "ratings_pending":     true,
  "notes":              ""
}

Status enum: completed | cancelled_by_user | cancelled_by_driver | failed | rerouted_with_extra_charge | partial_completion_user_drop_early


8. WIDGET

WIDGET TYPE:        intercity_ride_options
SOURCE:             src/widgets/types.ts
TYPE NAME:          IntercityRideOptionsPayload
RENDERED IN:        components/widgets/IntercityRideOptionsWidget.tsx

Default: 3 stacked rows showing vehicle_kind, arrival_eta, fare with allowance + toll breakdown summary, key amenities, FASTag adequacy badge. Tap row → confirmation card with full fare breakdown including state-border + night-halt lines → "Book". Then IntercityTrackingCard with leg progress + state-crossing indicators.


9. CACHING POLICY

Call TTL Rationale
get_intercity_estimates 60s demand moves slower than intracity
track_intercity_ride 0s always live
book_intercity_ride 0s
cancel_intercity_ride 0s
update_intercity_stops 0s
Failure responses 0s

10. ERROR CODES

Code HTTP Meaning TOMO behavior
NO_DRIVERS_AVAILABLE 503 none with valid permit + capacity retry with delay or fall back
OUT_OF_SERVICE_AREA 400 pickup or drop outside coverage surface
STATE_NOT_COVERED_BY_PERMIT 400 partner has no permit for required state surface, suggest alternate
OPTION_EXPIRED 410 ride_option_token invalid re-quote
PAYMENT_DECLINED 402 gateway rejection surface
OTP_FAILED 401 OTP-at-pickup mismatch surface
RIDE_NOT_FOUND 404 ride_ref doesn't exist surface
ALREADY_CANCELLED 409 duplicate cancel idempotent return
DRIVER_FATIGUE_LIMIT_REACHED 503 MV Act drive-hours exceeded partner reassigns
WEATHER_BLOCK 503 route flagged unsafe (cyclone, flood) suggest reschedule
FASTAG_INSUFFICIENT 400 route requires FASTag, balance low surface top-up CTA
STOP_OUT_OF_RANGE 400 intermediate stop > 50km off route reject or re-quote

11. SANDBOX → PRODUCTION CHECKLIST

[ ] All §2 inputs validated, request_id echoed
[ ] get_intercity_estimates returns ≥3 options for "Hyderabad → Bangalore, 4 pax, 6 bags"
[ ] All §4 required fields populated with REAL data
[ ] driver_kyc + vehicle_meta truthful + verifiable on demand
[ ] permit_states_covered ⊇ states_crossed validated
[ ] all_india_tourist_permit OR equivalent verified for multi-state
[ ] fitness_certificate + insurance + PUC + permit numbers all valid
[ ] book_intercity_ride returns ride_ref + driver assignment within SLA
[ ] track_intercity_ride returns location ≤10s old + leg progress accurate
[ ] cancel respects free_cancel_until_iso absolute window
[ ] update_intercity_stops handles ≥2 stops with correct fare recalc
[ ] CPC webhook arrives within 60s of ride completion
[ ] HMAC verification passes
[ ] No forbidden fields anywhere
[ ] Trip share URL works for ≥4h post-completion (vs 2h intracity)
[ ] SOS button tested with TOMO ops monitoring
[ ] Highway-assist telematics sample data shared for 100-call test
[ ] Fatigue compliance check tested on 14h+ trip simulation
[ ] No paid placement / sponsored signals
[ ] customer_support_24x7 verified by TOMO field call
[ ] Aggregator + tourist permit certificates uploaded for every state served

12. ANTI-FABRICATION RULES

RULE 1 — No paid placement signals.

RULE 2 — No fake driver ratings.
  rating_avg = unrounded float from real ride feedback. TOMO samples 1% rides.

RULE 3 — No fake intercity_rides_completed inflation.
  TOMO compares stated count against partner's CPC ledger; mismatch >5% = breach.

RULE 4 — Vehicle plate must match the actual vehicle dispatched.
  plate_masked at booking → must match vehicle that arrives.

RULE 5 — Fare displayed must be honored.
  is_upfront_fare=true means fare_locked_until_iso. Post-trip price hike (other
  than legitimate detour or extra stop) = breach.

RULE 6 — Driver KYC + permit claims must be verifiable.
  TOMO ops can request KYC + permit docs for any driver_id at any time. Failure
  to produce within 48h = breach.

RULE 7 — Fatigue compliance is non-negotiable.
  cumulative_drive_minutes_today must be reported truthfully. MV Act caps drive
  hours; partners ignoring this = breach.

RULE 8 — No surge gaming on intercity.
  Intercity fares are typically upfront, not surge-driven. Any post-quote upward
  fare adjustment requires a specific itemized line-item (extra stop, route change,
  state border, etc.) — surge_multiplier is NOT a valid intercity field.

RULE 9 — Cancel charges must match displayed thresholds.
  free_cancel_until_iso enforced server-side. Charging within window = breach.

RULE 10 — No commission-based response shaping.
  Same trip on TOMO and on partner's app must yield same fare.

RULE 11 — No state-border charge fabrication.
  state_border_charge_inr must be 0 if states_crossed has 1 element.
  Inventing border charges for same-state trips = breach.

RULE 12 — No driver swap mid-trip without alert.
  Any change of driver_id mid-trip must trigger a ride_safety alert + user
  consent. Silent swap = breach.

VERSION HISTORY

v1.0.0 — 2026-05-10 — Initial spec