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.0travel.book_flight

travel.book_flight — Full Intent Specification

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

Flight booking is fundamentally different from hotel: time-critical (cannot miss departure), seat-based pricing, multi-leg/round-trip/one-way, fare class hierarchy, baggage rules, ancillary upgrades. Distinct from mobility.book_intercity_ride because it crosses cities/countries via airline, with airport check-in protocol and airline-specific schedules. Locked structural fields: (a) journey_kind (one_way / round_trip / multi_city); (b) legs[] with origin/destination/scheduled_departure_iso; (c) fare_class enum (economy/premium_economy/business/first); (d) baggage_allowance block (cabin/checkin); (e) ancillaries (seat selection, meal, priority boarding); (f) pnr for held-bookings.


1. NATURAL LANGUAGE COVERAGE

Classifies IN

  • "flight from Hyderabad to Bangalore tomorrow"
  • "Indigo flight to Mumbai next Friday"
  • "round-trip flight Delhi-Goa June 15-22"
  • "cheapest flight to Singapore"
  • "business class to London"
  • "morning flight Hyderabad to Chennai"
  • "Air India flight to NYC"
  • "non-stop flight Bangalore Mumbai"
  • "multi-city: Hyderabad-Delhi-Mumbai"

Classifies OUT — borderline NO

  • "Hyderabad to Bangalore cab" → mobility.book_intercity_ride
  • "train to Vijayawada" → travel.book_train
  • "bus to Tirupati" → travel.book_bus
  • "tour package to Bali" → travel.book_package
  • "hotel near Bangalore airport" → travel.book_hotel

MULTI-INTENT TRIGGERS

  • "flight to Goa and hotel" → travel.book_flight + travel.book_hotel
  • "flight to Bangalore and airport pickup" → travel.book_flight + mobility.book_airport_transfer
  • "flight + visa for Singapore" → travel.book_flight + travel.book_visa_assistance

2. INPUT — TOMO → PROVIDER

{
  "intent":          "travel.book_flight",
  "intent_version":  "v1.0.0",
  "request_id":      "req_flt_8h2k_2026-05-10T08:00:00Z",
  "user_session_id": "anon_user_token_or_uid",

  "journey_kind":    "round_trip",

  "legs": [
    {
      "leg_index":              1,
      "origin_iata":            "HYD",
      "origin_city":            "Hyderabad",
      "origin_country_code":    "IN",
      "destination_iata":       "BLR",
      "destination_city":       "Bangalore",
      "destination_country_code": "IN",
      "scheduled_departure_iso": "2026-06-15T07:00:00+05:30",
      "scheduled_arrival_iso":   "2026-06-15T08:30:00+05:30",
      "preferred_departure_window_minutes": 120
    },
    {
      "leg_index":              2,
      "origin_iata":            "BLR",
      "origin_city":            "Bangalore",
      "origin_country_code":    "IN",
      "destination_iata":       "HYD",
      "destination_city":       "Hyderabad",
      "destination_country_code": "IN",
      "scheduled_departure_iso": "2026-06-18T19:00:00+05:30",
      "scheduled_arrival_iso":   "2026-06-18T20:30:00+05:30",
      "preferred_departure_window_minutes": 180
    }
  ],

  "passengers": {
    "adult_count":      2,
    "child_count_2_to_11": 1,
    "infant_count_under_2": 0,
    "passenger_kyc_kind": "indian_aadhaar"
  },

  "preferences": {
    "fare_class_acceptable":   ["economy", "premium_economy"],
    "fare_class_preferred":    "economy",
    "max_stops_per_leg":       0,
    "max_layover_minutes":     180,
    "airline_preferred":       ["6E", "AI"],
    "airline_avoid":           [],
    "alliance_preferred":      "any",
    "departure_time_window_kind": "morning",
    "arrival_time_window_kind":   "any",
    "non_stop_required":       true,
    "checked_baggage_count":   2,
    "checked_baggage_weight_kg": 23,
    "cabin_baggage_count":     2,
    "meal_preference":         "veg",
    "seat_kind_preferred":     "aisle",
    "wheelchair_assistance":   false,
    "unaccompanied_minor_assist": false,
    "priority_boarding_required": false,
    "free_cancellation_required": false,
    "free_change_required":    false,
    "budget_band":             "good",
    "budget_max_inr_total":    14000,
    "online_payment_required": true,
    "loyalty_program_card_kind": ["6e_rewards", "indigo_blue"],
    "loyalty_member_id":       ""
  },

  "context": {
    "user_locale":         "en-IN",
    "user_currency_pref":  "INR",
    "trip_purpose":        "leisure",
    "is_business_corporate": false,
    "trust_signals": {
      "is_repeat_customer":          true,
      "prior_flight_bookings":       8,
      "user_account_age_days":       312,
      "verified_phone":              true,
      "passport_verified":           true,
      "pan_verified":                true
    }
  }
}
Field Type Constraint Notes
intent string REQUIRED, equals "travel.book_flight"
journey_kind enum REQUIRED, STRICT one_way | round_trip | multi_city
legs array REQUIRED, ≥1 one_way=1, round_trip=2, multi_city=≥2
legs[].leg_index int REQUIRED, ≥1, contiguous
legs[].origin_iata string REQUIRED, 3 chars IATA code
legs[].origin_country_code string REQUIRED, ISO_3166_2
legs[].destination_iata string REQUIRED, 3 chars
legs[].destination_country_code string REQUIRED, ISO_3166_2
legs[].scheduled_departure_iso ISO_DATETIME REQUIRED
legs[].scheduled_arrival_iso ISO_DATETIME REQUIRED
legs[].preferred_departure_window_minutes int REQUIRED, ≥0 tolerance
passengers.adult_count int REQUIRED, ≥1
passengers.child_count_2_to_11 int REQUIRED, ≥0
passengers.infant_count_under_2 int REQUIRED, ≥0 typically lap-infant
passengers.passenger_kyc_kind enum REQUIRED, see §5 drives ID flow
preferences.fare_class_acceptable array REQUIRED, ≥1 see §5
preferences.max_stops_per_leg int REQUIRED, ≥0 0 = non-stop only
preferences.max_layover_minutes int REQUIRED, ≥0 for connecting flights
preferences.airline_preferred array REQUIRED, may be empty IATA airline codes
preferences.airline_avoid array REQUIRED, may be empty
preferences.alliance_preferred enum REQUIRED, see §5 star_alliance | skyteam | oneworld | any
preferences.departure_time_window_kind enum REQUIRED, see §5
preferences.non_stop_required bool REQUIRED hard filter when true
preferences.checked_baggage_count int REQUIRED, ≥0
preferences.checked_baggage_weight_kg int REQUIRED, ≥0
preferences.cabin_baggage_count int REQUIRED, ≥0
preferences.meal_preference enum REQUIRED, see §5
preferences.seat_kind_preferred enum REQUIRED, STRICT aisle | window | middle | any
preferences.wheelchair_assistance bool REQUIRED
preferences.unaccompanied_minor_assist bool REQUIRED
preferences.priority_boarding_required bool REQUIRED
preferences.free_cancellation_required bool REQUIRED
preferences.free_change_required bool REQUIRED
preferences.loyalty_program_card_kind array REQUIRED, may be empty see §5
context.is_business_corporate bool REQUIRED drives corporate-fare visibility

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


3. PROVIDER TOOLS

Tool 1: search_flights

PURPOSE:        return ranked flight options across airlines + fare classes
INPUT:          §2 request body
OUTPUT:         { results: FlightOption[], result_token, expires_at }
SLA:            p50 < 800ms, p95 < 2000ms
RATE LIMIT:     ≤ 1/sec per user

Tool 2: get_fare_rules_and_breakdown

PURPOSE:        full fare rules for a specific option (cancellation, change, baggage, etc.)
INPUT:          { option_id, request_id }
OUTPUT:         FareRulesAndBreakdown (§4)
SLA:            p95 < 800ms

Tool 3: lock_seat_or_ancillaries

PURPOSE:        select specific seat + add meals/priority boarding before booking
INPUT:          { option_id, seat_choices[], ancillaries[], request_id }
OUTPUT:         { ancillary_total_inr, locked_until_iso }
SLA:            p95 < 1500ms

Tool 4: book_flight

PURPOSE:        commit booking
INPUT:          { option_id, passenger_details, payment_token, request_id, idempotency_key, user_phone }
OUTPUT:         { pnr, booking_ref, status, total_inr, ticket_url, e_tickets_per_passenger[] }
SLA:            p95 < 8000ms
IDEMPOTENCY:    REQUIRED

Tool 5: track_pnr_status

PURPOSE:        live PNR status (cancellations, schedule changes, gate updates)
INPUT:          { pnr, request_id }
OUTPUT:         FlightTrack (§4)
SLA:            p95 < 600ms
RATE LIMIT:     ≤ 1 every 5min

Tool 6: cancel_flight

INPUT:          { booking_ref, reason, request_id }
OUTPUT:         { status, cancellation_charge_inr, refund_amount_inr, refund_processing_days }
SLA:            p95 < 4000ms

Tool 7: change_flight

PURPOSE:        modify dates / passenger / etc.
INPUT:          { booking_ref, modifications, request_id }
OUTPUT:         { revised_total_inr, change_fee_inr, fare_difference_inr, new_legs }
SLA:            p95 < 4000ms

Tool 8: web_check_in

PURPOSE:        partner-mediated web check-in
INPUT:          { booking_ref, passenger_pax_index, seat_choice_optional, request_id }
OUTPUT:         { boarding_pass_url, gate, seat_confirmed }
SLA:            p95 < 3000ms

All eight REQUIRED.


4. RESPONSE SHAPE

FlightOption

id:                               string, REQUIRED
option_token:                     string, REQUIRED
expires_at:                       ISO_DATETIME, REQUIRED

journey_kind:                     STRICT ENUM, REQUIRED
total_legs:                       int, REQUIRED, ≥1

legs:                             array, REQUIRED, ≥1
  - leg_index:                    int, REQUIRED
    leg_kind:                     STRICT ENUM, REQUIRED          # outbound | inbound | segment_n
    flight_segments:              array, REQUIRED, ≥1            # if non-stop, length 1
      - segment_index:            int, REQUIRED
        airline_iata:             string, REQUIRED
        airline_name:             string, REQUIRED
        flight_number:            string, REQUIRED               # "6E-432"
        operating_carrier_iata:   string, REQUIRED               # if codeshare
        aircraft_kind:            STRICT ENUM, REQUIRED          # see §5
        origin_iata:              string, REQUIRED
        origin_terminal:          string, REQUIRED
        destination_iata:         string, REQUIRED
        destination_terminal:     string, REQUIRED
        scheduled_departure_iso:  ISO_DATETIME, REQUIRED
        scheduled_arrival_iso:    ISO_DATETIME, REQUIRED
        duration_minutes:         int, REQUIRED, ≥1
        layover_minutes:          int, REQUIRED                  # 0 if first segment
    leg_total_duration_minutes:   int, REQUIRED
    leg_layover_count:            int, REQUIRED, ≥0
    leg_stops_count:              int, REQUIRED, ≥0
    leg_changes_required:         array<string>, REQUIRED, may be empty   # ["change in DEL"]
    is_overnight_leg:             boolean, REQUIRED

fare:
  base_fare_inr:                  INR_INTEGER, REQUIRED
  taxes_and_surcharges_inr:       INR_INTEGER, REQUIRED
  airport_dev_fee_inr:            INR_INTEGER, REQUIRED
  fuel_surcharge_inr:             INR_INTEGER, REQUIRED
  user_dev_fee_inr:               INR_INTEGER, REQUIRED
  meal_charge_inr:                INR_INTEGER, REQUIRED          # 0 if not selected
  seat_charge_inr:                INR_INTEGER, REQUIRED          # 0 if random
  baggage_charge_inr:             INR_INTEGER, REQUIRED          # 0 if standard
  priority_boarding_charge_inr:   INR_INTEGER, REQUIRED          # 0 if not selected
  insurance_inr:                  INR_INTEGER, REQUIRED          # 0 if not added
  convenience_fee_inr:            INR_INTEGER, REQUIRED
  gst_inr:                        INR_INTEGER, REQUIRED
  total_inr:                      INR_INTEGER, REQUIRED
  fare_breakdown_text:            string, REQUIRED
  is_upfront_fare:                boolean, REQUIRED
  fare_locked_until_iso:          ISO_DATETIME, REQUIRED

fare_class:                       STRICT ENUM, REQUIRED          # see §5
fare_brand_kind:                  STRICT ENUM, REQUIRED          # see §5; saver | flex | etc.

baggage_allowance:
  cabin_count_per_pax:            int, REQUIRED
  cabin_weight_kg_per_pax:        int, REQUIRED
  cabin_size_inches:              string, REQUIRED               # "55x35x25"
  checkin_count_per_pax:          int, REQUIRED
  checkin_weight_kg_per_pax:      int, REQUIRED
  checkin_size_inches:            string, REQUIRED
  excess_baggage_inr_per_kg:      INR_INTEGER, REQUIRED
  free_baggage_at_pre_book:       boolean, REQUIRED
  charged_baggage_at_pre_book:    boolean, REQUIRED

amenities_per_segment:            array, REQUIRED, ≥1
  - segment_index:                int, REQUIRED
    seat_pitch_inches:            int, REQUIRED
    seat_recline_degrees:         int, REQUIRED                  # 0 if no recline
    in_seat_power:                boolean, REQUIRED
    usb_charger:                  boolean, REQUIRED
    wifi_available:               boolean, REQUIRED
    wifi_kind:                    STRICT ENUM, REQUIRED          # see §5
    in_flight_entertainment:      STRICT ENUM, REQUIRED          # none | seat_screen | personal_device
    in_flight_meal_kind:          STRICT ENUM, REQUIRED          # see §5
    leg_room_kind:                STRICT ENUM, REQUIRED          # standard | extra | premium

cancellation:
  free_cancel_until_iso:          ISO_DATETIME, REQUIRED         # may equal "never_free"
  free_cancel_kind:               STRICT ENUM, REQUIRED          # see §5
  cancellation_fee_24h_inr:       INR_INTEGER, REQUIRED
  cancellation_fee_2h_inr:        INR_INTEGER, REQUIRED
  no_show_loss_pct:               float, REQUIRED, 0-1
  refund_processing_days:         int, REQUIRED, ≥0
  partial_refund_supported:       boolean, REQUIRED

change:
  free_change_supported:          boolean, REQUIRED
  change_fee_inr:                 INR_INTEGER, REQUIRED
  fare_difference_charged:        boolean, REQUIRED
  changes_allowed_count:          int, REQUIRED                  # -1 = unlimited

ancillaries_offered:
  meal_options:                   array<STRICT ENUM>, REQUIRED   # see §5
  meal_inr_per_segment:           INR_INTEGER, REQUIRED
  seat_selection_inr_per_segment: INR_INTEGER, REQUIRED          # 0 if free
  priority_boarding_inr:          INR_INTEGER, REQUIRED
  insurance_kinds_offered:        array<STRICT ENUM>, REQUIRED
  insurance_inr_per_pax:          INR_INTEGER, REQUIRED
  lounge_access_inr_per_pax:      INR_INTEGER, REQUIRED          # 0 if not offered
  fast_track_security_inr:        INR_INTEGER, REQUIRED

frequent_flyer:
  loyalty_eligible:               boolean, REQUIRED
  loyalty_program_kinds:          array<STRICT ENUM>, REQUIRED   # if eligible
  miles_earned_estimate:          int, REQUIRED
  miles_redeemed_estimate:        int, REQUIRED                  # if user opted

route_quality:
  on_time_arrival_30day_pct:      float, REQUIRED, 0-1           # for the airline+route pair
  cancellation_30day_pct:         float, REQUIRED, 0-1
  delay_minutes_30day_avg:        int, REQUIRED                  # average delay if delayed
  baggage_delivery_30day_avg_minutes: int, REQUIRED              # baggage carousel time
  pet_baggage_handling_record:    STRICT ENUM, REQUIRED          # excellent | good | fair | poor

trust:
  airline_iata_registered:        boolean, REQUIRED
  airline_iata_number:            string, REQUIRED
  airline_dgca_registered:        boolean, REQUIRED              # for India operations
  iosa_certified:                 boolean, REQUIRED              # IATA Operational Safety Audit
  faa_part_121_or_equivalent:     boolean, REQUIRED              # operational standards
  insurance_coverage_inr:         INR_INTEGER, REQUIRED
  partner_account_age_days:       int, REQUIRED, ≥0
  partner_flight_bookings_30d:    int, REQUIRED, ≥0
  partner_completion_rate_30d:    float, REQUIRED, 0-1

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_email:         string, REQUIRED
  customer_support_24x7:          boolean, REQUIRED
  in_app_chat_supported:          boolean, REQUIRED

FareRulesAndBreakdown

(Detailed fare rules, baggage rules, change conditions per leg per segment. All fields REQUIRED.)

FlightTrack

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

current_legs:                     array, REQUIRED, ≥1
  - leg_index:                    int, REQUIRED
    current_status:               STRICT ENUM, REQUIRED          # see §5
    delay_minutes_observed:       int, REQUIRED                  # 0 if on-time
    cancelled:                    boolean, REQUIRED
    rebooked_to_pnr:              string, REQUIRED               # may be empty
    gate:                         string, REQUIRED               # may be empty until announced
    boarding_iso:                 ISO_DATETIME, REQUIRED         # may equal future
    actual_departure_iso:         ISO_DATETIME, REQUIRED         # may equal future
    actual_arrival_iso:           ISO_DATETIME, REQUIRED

web_check_in_state:
  open:                           boolean, REQUIRED
  open_iso:                       ISO_DATETIME, REQUIRED
  close_iso:                      ISO_DATETIME, REQUIRED
  available_seats_count:          int, REQUIRED, ≥0

baggage_state:
  pre_paid:                       boolean, REQUIRED
  excess_alert:                   boolean, REQUIRED              # if user's baggage exceeds limit
  excess_charge_estimated_inr:    INR_INTEGER, REQUIRED

support_phone:                    string, REQUIRED
support_email:                    string, REQUIRED

Forbidden fields

paid_placement_score | sponsored_rank | promotion_priority |
fake_on_time_arrival_30day_pct | auto_inflate_loyalty_miles |
fake_cancellation_30day_pct | hidden_baggage_charge |
fake_iosa_certified | fake_dgca_registered | hidden_convenience_fee

5. CONTROLLED VOCABULARIES

journey_kind

one_way | round_trip | multi_city

passengers.passenger_kyc_kind

indian_aadhaar | indian_pan | indian_passport | foreign_passport |
voter_id | driving_license | other

preferences.fare_class_acceptable[] and FlightOption.fare_class

economy | premium_economy | business | first

FlightOption.fare_brand_kind

saver_no_meal_no_seat | standard | flex_changeable_no_change_fee |
flex_full_refund_with_fee | corporate | luxury_first |
ultra_low_cost | full_service

legs[].leg_kind

outbound | inbound | segment_1 | segment_2 | segment_n

flight_segments[].aircraft_kind

boeing_737 | boeing_777 | boeing_787 | boeing_747 |
airbus_a320 | airbus_a321 | airbus_a330 | airbus_a350 | airbus_a380 |
embraer_175 | bombardier_q400 | atr_72 | private_jet | other

preferences.alliance_preferred

star_alliance | skyteam | oneworld | none | any

preferences.departure_time_window_kind

early_morning_5_to_8 | morning_8_to_12 | afternoon_12_to_4 |
evening_4_to_8 | night_8_to_12 | red_eye_12_to_5 | any

preferences.meal_preference

veg | non_veg | jain | vegan | halal | kosher | gluten_free |
diabetic | low_sodium | seafood | none | any

preferences.seat_kind_preferred

aisle | window | middle | any

preferences.loyalty_program_card_kind[]

6e_rewards | indigo_blue | club_vistara | flying_returns_air_india |
spice_club | go_first_max | krisflyer | mileage_plus | privilege_club |
none

amenities_per_segment[].wifi_kind

none | free_basic | paid_premium | gate_to_gate_paid

amenities_per_segment[].in_flight_entertainment

none | seat_screen | personal_device | streaming_to_phone | sky_lounge_only

amenities_per_segment[].in_flight_meal_kind

none | snack_only | hot_meal | premium_meal | a_la_carte_paid | first_class_dining

amenities_per_segment[].leg_room_kind

standard | extra | premium | exit_row | bulkhead

cancellation.free_cancel_kind

never_free | within_24h_of_booking | within_grace_window_at_purchase |
within_72h_of_departure | within_24h_of_departure | always_partial

ancillaries_offered.meal_options[]

veg_indian | non_veg_indian | continental | jain_meal | vegan |
hindu_non_veg | muslim_meal | kosher | low_calorie | child_meal |
infant_meal | none

ancillaries_offered.insurance_kinds_offered[]

trip_protect | medical_emergency | flight_cancellation_protection |
baggage_loss_protection | none

frequent_flyer.loyalty_program_kinds[]

Same enum as preferences.loyalty_program_card_kind.

route_quality.pet_baggage_handling_record

excellent | good | fair | poor | not_supported

FlightTrack.status

booked_pending_payment | confirmed | rebooked | cancelled_by_user |
cancelled_by_airline | partially_completed | completed | refund_processing |
refund_completed | failed

FlightTrack.current_legs[].current_status

scheduled | on_time | delayed_minor | delayed_major | rerouted | cancelled |
boarding | departed | in_air | landed | arrived

cancel_flight.reason

user_changed_plans | medical_emergency | family_emergency |
schedule_change_too_disruptive | weather_block | found_alternative | other

merchant_id resolution order

1. Airline IATA + flight_number + scheduled_departure
2. Partner_id + ":" + booking_ref
3. PNR (post-booking)

6. TTBS DIMENSIONS

Per-domain weights (locked; flight)

travel (book_flight): { time: 0.40, taste: 0.10, budget: 0.30, safety: 0.20 }

TIME

SIGNALS USED:
  - legs[].scheduled_departure_iso match request      HARD FILTER (within ±preferred_window)
  - leg_total_duration_minutes (lower=better)         weight 0.30
  - leg_stops_count (lower=better)                    weight 0.20
  - max_stops_per_leg constraint                      HARD FILTER
  - max_layover_minutes constraint                    HARD FILTER
  - route_quality.on_time_arrival_30day_pct           weight 0.30
  - cancellation.refund_processing_days              weight 0.10

USER BAND HANDLING:
  - "earliest possible" → tighten arrival_iso
  - "flexible by ±2h" → relax window

TASTE

SIGNALS USED:
  - amenities match preferences (in_seat_power, wifi, IFE)  weight 0.30
  - airline_preferred match                                  weight 0.30
  - airline_avoid avoidance                                  HARD FILTER
  - amenities_per_segment.leg_room_kind                      weight 0.20
  - amenities_per_segment.in_flight_meal_kind                weight 0.10
  - alliance_preferred match                                  weight 0.10

HARD FILTERS:
  - meal_preference NOT in meal_options
  - non_stop_required AND leg_stops_count > 0

BUDGET

SIGNALS USED:
  - fare.total_inr vs band:
      ok    → ultra_low_cost / saver
      good  → standard / flex
      great → business / first / luxury_first
  - fare.is_upfront_fare=true                          weight 0.20
  - free_cancellation when requested                   HARD FILTER
  - free_change when requested                         HARD FILTER
  - included baggage match user's baggage_count        weight 0.20
  - convenience_fee_inr (lower=better)                 weight 0.10

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

SAFETY

SIGNALS USED:
  - airline_iata_registered AND dgca_registered (for IN routes) HARD FILTER
  - iosa_certified                                     weight 0.30
  - faa_part_121_or_equivalent                         weight 0.20
  - aircraft_kind (modern wide-body for long-haul preferred) weight 0.15
  - cancellation_30day_pct (lower=better)              weight 0.15
  - route_quality.delay_minutes_30day_avg              weight 0.20
  - airline insurance_coverage_inr ≥ 10M               weight 0.10
  - is_overnight_leg → safety scales 1.2x
  - wheelchair_assistance → drop airlines without service

HARD FILTERS:
  - dgca_registered=false for India domestic → drop
  - iosa_certified=false for international → drop

Hidden ranking factor

information_completeness_score weight 0.10.


7. COMPLETION CONTRACT

POST /api/v1/cpc/mcp_provider/{tomo_partner_id}
{
  "intent":            "travel.book_flight",
  "intent_version":    "v1.0.0",
  "external_id":       "FLT-XYZ",
  "amount_inr":         13800,
  "closed_at":         "2026-06-18T20:30:00+05:30",
  "request_id":        "req_flt_8h2k_...",
  "status":            "completed",
  "pnr":               "ABC123",
  "booking_ref":       "FLT-XYZ",
  "legs_completed":     2,
  "legs_total":         2,
  "actual_departure_iso_outbound": "2026-06-15T07:08:00+05:30",
  "actual_arrival_iso_outbound":   "2026-06-15T08:38:00+05:30",
  "actual_departure_iso_inbound":  "2026-06-18T19:08:00+05:30",
  "actual_arrival_iso_inbound":    "2026-06-18T20:38:00+05:30",
  "any_delay_event_minutes":       16,
  "any_cancellation_event":        false,
  "ancillaries_consumed":          ["meal_veg_indian", "seat_aisle"],
  "miles_earned":                  2400,
  "currency":          "INR",
  "fare_breakdown_total_inr": 13800,
  "ratings_pending":     true,
  "notes":              ""
}

Status enum: completed | cancelled_by_user | cancelled_by_airline_full_refund | rebooked_with_credit | partial_completion_one_leg_only | failed | refunded


8. WIDGET, 9. CACHING, 10. ERROR CODES, 11. CHECKLIST

(Standard pattern. Widget type: flight_options.)

Call TTL
search_flights 60s — fares + seats shift fast
get_fare_rules_and_breakdown 5min
lock_seat_or_ancillaries 0s
book_flight / cancel_flight / change_flight 0s
track_pnr_status 30s
web_check_in 0s

Universal codes + NO_FLIGHTS_AVAILABLE, OPTION_EXPIRED, PNR_NOT_FOUND, WEB_CHECK_IN_NOT_OPEN, BAGGAGE_OVER_LIMIT, SEAT_TAKEN, WEATHER_DELAY.


12. ANTI-FABRICATION RULES

RULE 1 — No paid placement.

RULE 2 — IOSA / DGCA claims real and current.

RULE 3 — On-time-arrival rate must come from carrier's actual operations data.

RULE 4 — Hidden baggage charge forbidden.
  baggage_charge_inr declared at search. Charging at airport beyond stated
  excess rates = breach.

RULE 5 — No phantom seat selection charges.
  seat_selection_inr_per_segment must reflect actual. Bait-and-switch =
  consumer-protection breach.

RULE 6 — Loyalty miles earned honest.
  Inflating miles_earned_estimate = breach.

RULE 7 — Cancellation refund processing within stated days.

RULE 8 — Free cancellation honored as stated.

RULE 9 — Schedule changes communicated honestly.
  Material schedule change >2h must trigger user notification + free rebook
  or refund per regulator (DGCA Aviation Customer Charter).

RULE 10 — No commission-based response shaping.

RULE 11 — Insurance coverage real and pays out.

RULE 12 — Aircraft fitness real.
  aircraft_kind claimed must match dispatched plane. Substituting with older
  plane silently = breach.

VERSION HISTORY

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