mobility.book_recurring_commute — Full Intent Specification
INTENT NAMESPACE: mobility
INTENT NAME: book_recurring_commute
FULL ID: mobility.book_recurring_commute
VERSION: v1.0.0
STATUS: draft (V2 marker)
LAST UPDATED: 2026-05-10
TTBS WEIGHTS: time 0.40 · taste 0.20 · budget 0.30 · safety 0.10
Recurring commute is a subscription-style intent for daily/weekly rides between two fixed locations, on a defined schedule, billed periodically. It differs from per-ride booking in five locked ways: (a) recurrence_pattern defines the schedule; (b) billing_period aggregates many rides into one charge; (c) assigned_driver_kind supports dedicated for repeat-customer comfort; (d) pause/resume + skip-day are first-class operations; (e) safety weight is LOWER than other mobility (0.10) — the user has already vetted this pattern + driver across N prior rides; taste rises (driver familiarity is what users actually buy here). V2 status marker — partner integrations should be cautious until at least 1 partner ships this end-to-end.
1. NATURAL LANGUAGE COVERAGE
Classifies IN
- "subscribe to daily ride to office"
- "recurring commute Mon-Fri to HITEC City"
- "book a daily cab to office for the month"
- "monthly cab subscription home to office"
- "set up weekday commute rides"
- "every morning ride to work, can you book it"
- "fixed driver for daily commute"
- "weekly cab pass for office"
- "shuttle subscription Madhapur to Banjara Hills"
Classifies OUT — borderline NO
- "cab to office today" →
mobility.book_intracity_ride(one-off) - "rent car for the week" →
mobility.book_self_drive - "8h chauffeur block" →
mobility.book_chauffeur_hourly - "school bus subscription" → no mobility intent (logistics/school-bus is a different domain)
MULTI-INTENT TRIGGERS
- "daily commute subscription and FASTag top-up" →
mobility.book_recurring_commute+pay.fastag_topup - "monthly cab pass to office and verify my KYC" →
mobility.book_recurring_commute+compliance.verify_kyc
2. INPUT — TOMO → PROVIDER
{
"intent": "mobility.book_recurring_commute",
"intent_version": "v1.0.0",
"request_id": "req_rec_2k7p_2026-05-10T08:00:00Z",
"user_session_id": "anon_user_token_or_uid",
"subscription_kind": "monthly_weekday",
"pickup": {
"lat": 17.4435,
"lng": 78.3772,
"address": "Near Madhapur metro station",
"neighborhood": "Madhapur",
"city": "Hyderabad",
"pincode": "500081",
"state_code": "TS",
"country_code": "IN",
"place_kind": "home",
"instructions": "Pickup at building gate B"
},
"drop": {
"lat": 17.4124,
"lng": 78.4470,
"address": "Hitex Trade Center, Banjara Hills",
"neighborhood": "Banjara Hills",
"city": "Hyderabad",
"pincode": "500034",
"state_code": "TS",
"country_code": "IN",
"place_kind": "office",
"instructions": "Office gate 3"
},
"recurrence": {
"pattern": "mon_to_fri",
"ride_kind_per_day": "round_trip",
"morning_pickup_window": {
"start_iso_local": "08:30:00",
"end_iso_local": "09:00:00"
},
"evening_pickup_window": {
"start_iso_local": "18:30:00",
"end_iso_local": "19:30:00"
},
"skip_dates": ["2026-05-15", "2026-05-22"],
"active_from_iso": "2026-05-12",
"active_until_iso": "2026-06-12"
},
"billing": {
"period_kind": "monthly",
"auto_renew": true,
"billing_day_of_month": 10
},
"assigned_driver_kind": "preferred_pool",
"preferred_driver_id": "",
"party": {
"passenger_count": 1,
"luggage_pieces": 1,
"luggage_size": "backpack"
},
"preferences": {
"vehicle_kinds_acceptable": ["sedan", "hatchback"],
"vehicle_kinds_preferred": ["sedan"],
"budget_band": "good",
"budget_max_inr_per_period": 18000,
"max_eta_minutes": 5,
"ac_required": true,
"female_driver_required": false,
"ev_only": false,
"wheelchair_accessible_required": false,
"no_pool": true,
"driver_must_speak_locales": ["en-IN", "te-IN"],
"max_seat_capacity_min": 4
},
"route_context": {
"estimated_distance_km": 18.5,
"estimated_duration_min": 35,
"involves_toll": false,
"involves_highway": false,
"is_late_night": false
},
"context": {
"user_locale": "en-IN",
"user_currency_pref": "INR",
"trip_purpose": "commute_to_work",
"trust_signals": {
"is_repeat_customer": true,
"prior_rides_with_partner": 47,
"user_account_age_days": 312,
"fastag_present": false,
"fastag_balance_inr": 0
}
}
}
| Field | Type | Constraint | Notes |
|---|---|---|---|
intent |
string | REQUIRED, equals "mobility.book_recurring_commute" |
|
subscription_kind |
enum | REQUIRED, see §5 | |
pickup.place_kind |
enum | REQUIRED | |
drop.place_kind |
enum | REQUIRED | |
recurrence.pattern |
enum | REQUIRED, see §5 | |
recurrence.ride_kind_per_day |
enum | REQUIRED, STRICT one_way_morning | one_way_evening | round_trip |
|
recurrence.morning_pickup_window.start_iso_local |
string | REQUIRED, "HH:MM:SS" | |
recurrence.morning_pickup_window.end_iso_local |
string | REQUIRED | |
recurrence.evening_pickup_window |
object | REQUIRED, may be empty if one_way_morning | |
recurrence.skip_dates |
array |
REQUIRED, may be empty | |
recurrence.active_from_iso |
ISO_DATE | REQUIRED | |
recurrence.active_until_iso |
ISO_DATE | REQUIRED | must be > active_from |
billing.period_kind |
enum | REQUIRED, STRICT weekly | monthly | quarterly |
|
billing.auto_renew |
bool | REQUIRED | |
billing.billing_day_of_month |
int | REQUIRED, 1-31 | drives charge cycle |
assigned_driver_kind |
enum | REQUIRED, see §5 | |
preferred_driver_id |
string | REQUIRED, may be empty | only relevant if dedicated |
preferences.budget_max_inr_per_period |
INR_INTEGER | REQUIRED | matches billing.period_kind |
preferences.max_eta_minutes |
int | REQUIRED, ≥0 | per-ride pickup tolerance |
Anti-fabrication preamble (universal): no paid placement, no urgency text, no commission-influenced fields.
3. PROVIDER TOOLS
Tool 1: get_recurring_commute_estimates
PURPOSE: return subscription options across vehicle kinds + driver kinds
INPUT: §2 request body
OUTPUT: { options: RecurringCommuteOption[], result_token, expires_at }
SLA: p50 < 800ms, p95 < 1500ms
RATE LIMIT: ≤ 1/sec per user
Tool 2: book_recurring_commute
PURPOSE: commit a subscription
INPUT: { option_id, payment_token, request_id, idempotency_key, user_phone, otp_required }
OUTPUT: { subscription_ref, status, fare_quote, first_ride_iso, dedicated_driver_assigned, billing_setup_status }
SLA: p95 < 5000ms
IDEMPOTENCY: REQUIRED on idempotency_key
Tool 3: get_subscription_state
PURPOSE: live subscription state with rides-this-period summary
INPUT: { subscription_ref, request_id }
OUTPUT: RecurringCommuteState (§4)
SLA: p95 < 600ms
RATE LIMIT: ≤ 1 every 60s
Tool 4: pause_subscription
PURPOSE: pause the subscription temporarily
INPUT: { subscription_ref, pause_from_iso, pause_until_iso, reason, request_id }
OUTPUT: { status, billing_credit_inr, resume_iso }
SLA: p95 < 1500ms
Tool 5: resume_subscription
PURPOSE: resume a paused subscription
INPUT: { subscription_ref, resume_from_iso, request_id }
OUTPUT: { status, next_billing_iso, next_ride_iso }
SLA: p95 < 1500ms
Tool 6: skip_ride_for_date
PURPOSE: user skips a single day
INPUT: { subscription_ref, skip_date_iso, request_id }
OUTPUT: { status, billing_adjustment_inr }
SLA: p95 < 1000ms
Tool 7: update_subscription_window
PURPOSE: modify pickup window (e.g., user shifts WFH)
INPUT: { subscription_ref, new_morning_window, new_evening_window, request_id }
OUTPUT: { status, effective_from_iso, fare_delta_inr }
SLA: p95 < 1500ms
Tool 8: cancel_subscription
PURPOSE: cancel
INPUT: { subscription_ref, reason, cancel_immediately, request_id }
OUTPUT: { status, refund_amount_inr, last_ride_iso, refund_processing_days }
SLA: p95 < 2000ms
Tool 9: get_ride_for_date
PURPOSE: get the ride record for a specific date (today's or upcoming)
INPUT: { subscription_ref, date_iso, leg_kind, request_id }
OUTPUT: { ride_ref, driver, vehicle, eta, status }
SLA: p95 < 600ms
Tool 10: track_ride
PURPOSE: live tracking of an active ride within the subscription
INPUT: { ride_ref, request_id }
OUTPUT: RideTrack (same shape as mobility.book_intracity_ride §4)
SLA: p95 < 400ms
All ten REQUIRED.
4. RESPONSE SHAPE
RecurringCommuteOption (returned by get_recurring_commute_estimates)
id: string, REQUIRED
option_token: string, REQUIRED
expires_at: ISO_DATETIME, REQUIRED
subscription_kind: STRICT ENUM, REQUIRED
vehicle_kind: STRICT ENUM, REQUIRED # see §5
vehicle_class: STRICT ENUM, REQUIRED
display_label: string, REQUIRED # "Daily Sedan Pass"
seat_capacity: int, REQUIRED, ≥1
luggage_capacity: STRICT ENUM, REQUIRED
ride_summary:
expected_rides_in_period: int, REQUIRED, ≥1
rides_per_week: float, REQUIRED, ≥0
total_period_distance_km: float, REQUIRED
per_ride_distance_km: float, REQUIRED
assigned_driver:
kind: STRICT ENUM, REQUIRED # any | preferred_pool | dedicated
dedicated_driver_id: string, REQUIRED # may be empty
dedicated_driver_name: string, REQUIRED # may be empty
pool_size: int, REQUIRED, ≥0 # 0 if dedicated
fallback_to_pool_on_unavailable: boolean, REQUIRED
fare:
total_period_inr: INR_INTEGER, REQUIRED
per_ride_avg_inr: INR_INTEGER, REQUIRED
base_per_ride_inr: INR_INTEGER, REQUIRED # standalone-equivalent rate
subscription_discount_pct: float, REQUIRED, 0-1 # e.g., 0.15 = 15% off
toll_inr_per_period: INR_INTEGER, REQUIRED
ac_charge_inr_per_period: INR_INTEGER, REQUIRED
late_night_charge_inr_per_period: INR_INTEGER, REQUIRED
platform_fee_inr_per_period: INR_INTEGER, REQUIRED
gst_inr_per_period: INR_INTEGER, REQUIRED
fare_breakdown_text: string, REQUIRED
is_upfront_fare: boolean, REQUIRED # locked for the period
fare_locked_until_iso: ISO_DATETIME, REQUIRED
billing:
period_kind: STRICT ENUM, REQUIRED
charge_iso: ISO_DATETIME, REQUIRED # next charge
payment_method_required: STRICT ENUM, REQUIRED # see §5
prorated_first_period: boolean, REQUIRED
proration_method: STRICT ENUM, REQUIRED
refund_policy: STRICT ENUM, REQUIRED # see §5
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
vehicle_meta:
age_years: int, REQUIRED
fuel_kind: STRICT ENUM, REQUIRED
ev_battery_charge_pct: int, REQUIRED, 0-100
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
permit_kind: STRICT ENUM, REQUIRED
vehicle_color: string, REQUIRED
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
recurring_subscriptions_served: 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
on_time_arrival_rate_30d: float, REQUIRED, 0-1
driver_kyc:
dl_verified: boolean, REQUIRED
dl_number_masked: string, REQUIRED
dl_valid_until_iso: ISO_DATE, 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
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
cancellation:
free_cancel_within_first_n_rides: int, REQUIRED, ≥0 # grace period
cancel_charge_inr_per_remaining_period: INR_INTEGER, REQUIRED
exit_charge_inr: INR_INTEGER, REQUIRED # one-time at cancel
refund_processing_days: int, REQUIRED, ≥0
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_recurring_subscriptions_served_30d: int, REQUIRED, ≥0
partner_subscription_completion_rate_30d: float, REQUIRED, 0-1
RecurringCommuteState (returned by get_subscription_state)
subscription_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
rides_completed_this_period: int, REQUIRED, ≥0
rides_skipped_by_user: int, REQUIRED, ≥0
rides_failed_by_partner: int, REQUIRED, ≥0
rides_remaining_this_period: int, REQUIRED, ≥0
current_period_inr_charged: INR_INTEGER, REQUIRED, ≥0
next_period_inr: INR_INTEGER, REQUIRED
driver_assignment:
current_driver_id: string, REQUIRED
current_driver_name: string, REQUIRED
is_dedicated: boolean, REQUIRED
consecutive_rides_with_driver: int, REQUIRED, ≥0
next_ride:
date_iso: ISO_DATE, REQUIRED
leg_kind: STRICT ENUM, REQUIRED # morning | evening
pickup_window_start_iso: ISO_DATETIME, REQUIRED
pickup_window_end_iso: ISO_DATETIME, REQUIRED
expected_driver_id: string, REQUIRED
expected_vehicle_label: string, REQUIRED
paused:
is_paused: boolean, REQUIRED
paused_from_iso: ISO_DATETIME, REQUIRED # may equal active iso
paused_until_iso: ISO_DATETIME, REQUIRED # may equal active iso
billing_state:
next_charge_iso: ISO_DATETIME, REQUIRED
payment_method_status: STRICT ENUM, REQUIRED # see §5
auto_renew_active: 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_recurring_subscriptions_served | partner_paid_for_top_listing |
fake_subscription_completion_rate_30d | hidden_exit_charge_inr |
silently_charged_skipped_rides
5. CONTROLLED VOCABULARIES
subscription_kind
weekly_weekday | weekly_full | monthly_weekday | monthly_full |
quarterly_weekday | quarterly_full | custom
vehicle_kind
hatchback | sedan | suv | premium_sedan | ev_hatchback | ev_sedan
Recurring is typically economy/comfort tier — premium and luxury are unusual but allowed.
vehicle_class
economy | comfort | premium
RecurringCommuteOption.luggage_capacity
small | medium | large
pickup.place_kind and drop.place_kind
home | office | school | college | gym | mall | landmark | other
recurrence.pattern
mon_to_fri | mon_to_sat | every_day | mon_wed_fri | tue_thu | weekends_only |
custom_days
If custom_days, partner provides additional recurrence.custom_days_array field listing weekday numbers (0=Mon, 6=Sun).
recurrence.ride_kind_per_day
one_way_morning | one_way_evening | round_trip
assigned_driver_kind
any | preferred_pool | dedicated
any— partner picks driver per ridepreferred_pool— partner maintains 2-3 driver pool, rotates among themdedicated— single driver assigned for the entire period (premium tier)
billing.period_kind
weekly | monthly | quarterly
billing.payment_method_required
upi_autopay | card_recurring_mandate | netbanking_emandate | wallet_auto_recharge
billing.proration_method
exact_per_ride_count | flat_first_period | no_proration
billing.refund_policy
unused_rides_credited | unused_rides_refunded_60pct | unused_rides_refunded_full |
no_refund_after_period_starts
vehicle_meta.fuel_kind
petrol | diesel | cng | lpg | ev_full | hybrid | bs6_petrol | bs6_diesel
vehicle_meta.emission_norm
bs3 | bs4 | bs6 | ev | unknown_legacy
vehicle_meta.permit_kind
contract_carriage | aggregator_permit | tourist
vehicle_meta.vehicle_class_certification
commercial_yellow_plate | aggregator_white_with_yellow_strip | tourist
vehicle_amenities.child_seat_kind
none | infant | toddler | booster | universal
driver_meta.age_band
21-30 | 31-40 | 41-55 | 56+
RecurringCommuteState.status
pending_first_ride | active | paused_by_user | paused_by_partner |
billing_failed | renewal_failed | cancelled_by_user | cancelled_by_partner |
expired | failed
RecurringCommuteState.next_ride.leg_kind
morning | evening
RecurringCommuteState.billing_state.payment_method_status
active | pending_authorization | failed_last_charge | mandate_revoked
cancel_subscription.reason
user_changed_address | user_changed_office | user_changed_schedule |
unsatisfied_with_driver | unsatisfied_with_vehicle | unsatisfied_with_pricing |
moved_city | found_alternative | medical | other
pause_subscription.reason
travel | medical | wfh_period | sabbatical | other
context.trip_purpose
commute_to_work | commute_to_home | commute_to_school | commute_to_college |
commute_to_gym | other
6. TTBS DIMENSIONS
Per-domain weights (locked; recurring override)
mobility (recurring_commute): { time: 0.40, taste: 0.20, budget: 0.30, safety: 0.10 }
Time stays high (commute punctuality is the whole point). Safety drops to 0.10 — repeat-customer pattern means user has already vetted partner across many rides; safety filters still apply as HARD FILTERS but don't drive ranking. Taste up — driver familiarity is what users actually buy here.
TIME
SIGNALS USED:
- driver_meta.on_time_arrival_rate_30d weight 0.40
- max_eta_minutes met during pilot rides weight 0.20
- assigned_driver_kind=dedicated (most predictable) weight 0.20
- cancellation.free_cancel_within_first_n_rides weight 0.10
- billing.refund_policy permissiveness weight 0.10
USER BAND HANDLING:
- "very strict morning timing" → on_time rate threshold ≥ 0.95
TASTE
SIGNALS USED:
- assigned_driver.kind ranking: dedicated > preferred_pool > any weight 0.30
- driver_meta.rating_avg weight 0.20
- vehicle.year_of_manufacture weight 0.10
- vehicle_amenities match w/ user prefs weight 0.20
- driver_meta.recurring_subscriptions_served (high=experienced) weight 0.10
- languages_spoken match user_locale weight 0.10
HARD FILTERS:
- ac_required + ac=false → drop
- female_driver_required → drop male-driver pools
BUDGET
SIGNALS USED:
- fare.total_period_inr vs band:
ok → hatchback / shared (rare for recurring)
good → sedan / hatchback dedicated
great → premium_sedan dedicated / EV
- fare.subscription_discount_pct (higher=better) weight 0.30
- fare.is_upfront_fare=true weight 0.20
- cancellation.cancel_charge_inr_per_remaining_period weight 0.20
- cancellation.exit_charge_inr (lower=better) weight 0.15
- billing.refund_policy permissiveness weight 0.15
HARD FILTERS:
- fare.total_period_inr > preferences.budget_max_inr_per_period → drop
SAFETY
SIGNALS USED:
- driver_kyc.background_check_passed HARD FILTER
- driver_kyc.dl_verified HARD FILTER
- vehicle_meta.comprehensive_insurance HARD FILTER
- vehicle_meta.permit_kind valid HARD FILTER
- vehicle_meta.fitness_certificate_valid_until_iso > active_until HARD FILTER
- safety_features.sos_button_in_app weight 0.20
- safety_features.cctv_in_cab weight 0.20
- safety_features.driver_drowsiness_detection weight 0.20
- driver_meta.female_driver
(boosted if female_driver_required) weight 0.20
- driver_meta.recurring_subscriptions_served (≥10) weight 0.20
- is_late_night → safety scales 1.3x
Note: safety weight in domain TTBS is 0.10, but HARD FILTERS still apply.
Hidden ranking factor
information_completeness_score weight 0.10.
7. COMPLETION CONTRACT
Recurring intent has TWO completion contract events: per-period billing settlement AND subscription closure. Both POST to the same endpoint with different event_kind.
Per-period settlement (fires after each billing period)
POST /api/v1/cpc/mcp_provider/{tomo_partner_id}
X-TOMO-Timestamp: <ms>
X-TOMO-Signature: sha256=<hex>
{
"intent": "mobility.book_recurring_commute",
"intent_version": "v1.0.0",
"event_kind": "period_settled",
"external_id": "OLA-RECUR-XYZ",
"subscription_ref": "OLA-RECUR-XYZ",
"amount_inr": 16800,
"closed_at": "2026-06-10T00:01:00+05:30",
"request_id": "req_rec_2k7p_...",
"status": "period_completed",
"period_start_iso": "2026-05-12T00:00:00+05:30",
"period_end_iso": "2026-06-09T23:59:59+05:30",
"rides_completed": 42,
"rides_skipped_by_user": 4,
"rides_failed_by_partner": 0,
"currency": "INR",
"notes": ""
}
Subscription closure
POST /api/v1/cpc/mcp_provider/{tomo_partner_id}
X-TOMO-Timestamp: <ms>
X-TOMO-Signature: sha256=<hex>
{
"intent": "mobility.book_recurring_commute",
"intent_version": "v1.0.0",
"event_kind": "subscription_closed",
"external_id": "OLA-RECUR-XYZ",
"subscription_ref": "OLA-RECUR-XYZ",
"amount_inr": 50400,
"closed_at": "2026-08-12T00:00:00+05:30",
"request_id": "req_rec_2k7p_...",
"status": "completed",
"subscription_started_at": "2026-05-12T00:00:00+05:30",
"subscription_ended_at": "2026-08-11T23:59:59+05:30",
"total_periods_billed": 3,
"total_rides_completed": 126,
"total_rides_skipped": 12,
"total_rides_failed": 1,
"currency": "INR",
"fare_breakdown_total_inr": 50400,
"ratings_pending": true,
"notes": ""
}
Status enum:
- per-period:
period_completed | period_partial_refund_credited | period_billing_failed - subscription:
completed | cancelled_by_user_within_grace | cancelled_by_user_after_grace | cancelled_by_partner | expired_no_renew | failed
8. WIDGET
WIDGET TYPE: recurring_commute_options
SOURCE: src/widgets/types.ts
TYPE NAME: RecurringCommuteOptionsPayload
RENDERED IN: components/widgets/RecurringCommuteOptionsWidget.tsx
Default: 3 stacked rows showing subscription kind label (e.g., "Monthly weekday pass — 22 rides"), vehicle_kind, fare with per-ride-avg + period-total, driver assignment kind badge (dedicated/preferred_pool/any), discount %, on-time rate strip. Tap row → confirmation card with full subscription terms (period, billing day, refund policy, cancel grace) → "Subscribe". Then RecurringCommuteDashboard with period progress + pause/resume + skip-day controls.
9. CACHING POLICY
| Call | TTL | Rationale |
|---|---|---|
get_recurring_commute_estimates |
300s | inventory + pricing move slowly for subscriptions |
get_subscription_state |
30s | period summary doesn't change minute-to-minute |
book_recurring_commute |
0s | |
pause_subscription / resume_subscription |
0s | |
skip_ride_for_date |
0s | |
update_subscription_window |
0s | |
cancel_subscription |
0s | |
get_ride_for_date |
5s | |
track_ride |
0s | always live during ride |
| Failure responses | 0s |
10. ERROR CODES
| Code | HTTP | Meaning | TOMO behavior |
|---|---|---|---|
NO_DRIVERS_AVAILABLE |
503 | no driver pool covers the route + window | retry or fall back |
OUT_OF_SERVICE_AREA |
400 | pickup or drop outside coverage | surface |
OPTION_EXPIRED |
410 | option_token invalid | re-quote |
PAYMENT_METHOD_REQUIRED |
402 | no upi_autopay / card mandate set up | surface |
MANDATE_AUTH_FAILED |
402 | bank mandate auth declined | surface, retry |
SUBSCRIPTION_NOT_FOUND |
404 | subscription_ref doesn't exist | surface |
ALREADY_CANCELLED |
409 | duplicate cancel | idempotent return |
ALREADY_PAUSED |
409 | duplicate pause | idempotent return |
BILLING_FAILED |
402 | last billing cycle declined | surface, dunning flow |
MANDATE_REVOKED |
401 | user revoked mandate at bank | surface, re-onboard |
WINDOW_INFEASIBLE |
400 | requested pickup window not coverable by any driver | surface |
RIDE_NOT_SCHEDULED |
404 | get_ride_for_date for non-scheduled day | surface |
DEDICATED_DRIVER_UNAVAILABLE |
503 | dedicated driver unavailable; fallback | partner reassigns from pool |
11. SANDBOX → PRODUCTION CHECKLIST
[ ] All §2 inputs validated, request_id echoed
[ ] get_recurring_commute_estimates returns ≥3 options for "weekday HITEC City → Banjara Hills"
[ ] All §4 required fields populated with REAL data
[ ] driver_kyc + vehicle_meta truthful + verifiable
[ ] permit_kind valid for daily commercial commute
[ ] fitness_certificate valid through active_until
[ ] book_recurring_commute returns subscription_ref + first_ride within SLA
[ ] UPI Autopay or card mandate setup tested in sandbox
[ ] First ride dispatched within window on active_from_iso
[ ] track_ride works for any ride within subscription
[ ] pause/resume/skip flows tested with billing adjustments
[ ] update_subscription_window respects effective_from_iso boundary
[ ] cancel respects free_cancel_within_first_n_rides grace
[ ] Per-period CPC webhook fires on each billing close within 24h
[ ] Subscription-closure CPC webhook fires within 60s of cancel/expire
[ ] HMAC verification passes
[ ] No forbidden fields anywhere
[ ] Dedicated-driver assignment honored across periods
[ ] Fallback-to-pool tested when dedicated driver unavailable
[ ] partner_subscription_completion_rate_30d audited against TOMO sample
[ ] No paid placement / sponsored signals
[ ] customer_support_24x7 verified by TOMO field call
12. ANTI-FABRICATION RULES
RULE 1 — No paid placement signals.
RULE 2 — No fake driver ratings.
RULE 3 — No fake on_time_arrival_rate_30d.
Rate must come from partner's CPC ledger of completed rides. TOMO samples
1% rides for late vs on-time validation. Mismatch >5% = breach.
RULE 4 — No fake recurring_subscriptions_served inflation.
RULE 5 — No fake subscription_completion_rate_30d.
RULE 6 — Vehicle plate must match dispatched vehicle.
RULE 7 — Fare displayed must be honored.
is_upfront_fare=true means fare_locked_until_iso. Per-period fare cannot
change mid-period without explicit user consent + re-onboarding.
RULE 8 — Driver KYC + permit claims must be verifiable.
RULE 9 — Cancel charges must match displayed thresholds.
Free cancel within first N rides cannot be reduced retroactively.
RULE 10 — No commission-based response shaping.
RULE 11 — Skipped rides are credited honestly.
rides_skipped_by_user must be reflected in billing_credit_inr per the
proration method declared at booking. Charging full period when user
skipped 8 of 22 rides = breach.
RULE 12 — Failed rides are not silently absorbed.
rides_failed_by_partner > 0 must trigger user-visible billing credit AND
partner-side service-level penalty per partner contract. Hiding failed
rides to preserve completion_rate_30d = severe breach.
RULE 13 — Dedicated driver promise honored or compensated.
assigned_driver_kind=dedicated requires same driver across rides except
in declared exception cases (driver leave, illness). Substituting silently
> 5% of rides = breach + automatic refund tier.
RULE 14 — Auto-renew is honest.
billing.auto_renew=true requires partner to notify user 7 days before next
charge. Auto-charging without notification + ability to cancel = breach.
RULE 15 — Pause/resume math is honest.
pause_subscription credit = (paused_days / total_period_days) × period_inr,
rounded down to whole rupees. Inflating denominator or undercounting
paused_days = billing fraud.
RULE 16 — Mandate revocation respected.
If user revokes mandate at bank, partner cannot continue charging via
fallback method without explicit user re-onboarding.
VERSION HISTORY
v1.0.0 — 2026-05-10 — Initial spec (V2 marker — partner integration cautious until 1 partner ships end-to-end)