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.0food.book_dine_in

food.book_dine_in — Full Intent Specification

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

Dine-in is restaurant table reservation — different shape from delivery in five locked ways: (a) table + slot are the listing unit, not dish + rider; (b) cover charge / deposit / table_kind replace delivery_fee; (c) meal_period + duration_minutes define the booking window; (d) accessibility + dietary at the dish level become accessibility + dietary at the seating level (private dining, allergen-protected kitchen); (e) safety stays meaningful (food handling) but adds physical-safety concerns absent in delivery (well-lit access, female-friendly hours).


1. NATURAL LANGUAGE COVERAGE

Classifies IN

  • "table for 4 at Paradise tonight"
  • "book a table at Olive Beach for 8pm"
  • "reservation at Farzi Cafe Saturday"
  • "dinner reservation for 2 at 7:30pm"
  • "table for date night Friday"
  • "lunch at Indian Accent tomorrow"
  • "I want to dine in at Bombay Brasserie"
  • "book a private dining room for 8 people"
  • "reservation for 6 at Paradise biryani 8:30pm"

Classifies OUT — borderline NO

  • "biryani for dinner" → food.order_delivery
  • "tiffin subscription" → food.subscribe_tiffin
  • "chef at home for a dinner party" → food.book_chef_at_home
  • "table at Paradise with 30% off offer" → food.book_dine_in_with_offer
  • "catering for a wedding" → food.book_catering_event

MULTI-INTENT TRIGGERS

  • "book Olive Beach for 8 and a cab" → food.book_dine_in + mobility.book_intracity_ride
  • "table at Indian Accent and book a hotel nearby" → food.book_dine_in + travel.book_hotel
  • "reservation at Paradise with anniversary cake" → food.book_dine_in + food.order_cake_or_special

2. INPUT — TOMO → PROVIDER

{
  "intent":          "food.book_dine_in",
  "intent_version":  "v1.0.0",
  "request_id":      "req_dn_8h2k_2026-05-10T18:00:00Z",
  "user_session_id": "anon_user_token_or_uid",

  "search_area": {
    "lat":            17.4435,
    "lng":            78.3772,
    "radius_km":      5,
    "city":           "Hyderabad",
    "state_code":     "TS",
    "country_code":   "IN"
  },

  "scheduled_for_iso":   "2026-05-10T20:30:00+05:30",
  "duration_minutes":     90,
  "meal_period":          "dinner",

  "party": {
    "adult_count":         3,
    "child_count":         1,
    "infant_count":        0,
    "high_chair_required": true,
    "wheelchair_required": false,
    "occasion":            "birthday",
    "dietary_filters":     ["veg"],
    "allergens_to_avoid":  ["peanuts"]
  },

  "preferences": {
    "cuisines_wanted":            ["hyderabadi", "south_indian", "north_indian"],
    "budget_band":                "good",
    "budget_max_inr_per_head":     800,
    "preferred_seating_kind":     ["indoor_ac", "outdoor_garden", "private_dining"],
    "atmosphere_kinds_wanted":    ["family_friendly", "romantic", "quiet"],
    "spice_level_max":            "medium",
    "no_onion_no_garlic":         false,
    "jain_meal_required":         false,
    "halal_required":             false,
    "alcohol_served_acceptable":  true,
    "music_kind_acceptable":      ["soft_instrumental", "lounge", "no_music"],
    "valet_parking_required":     false,
    "kids_menu_required":         true
  },

  "context": {
    "user_locale":          "en-IN",
    "user_currency_pref":   "INR",
    "is_weekend":           false,
    "is_festival_day":      false,
    "weather_celsius":      29,
    "rain_probability_pct": 10,
    "trust_signals": {
      "is_repeat_customer":          true,
      "prior_dine_in_with_partner":  2,
      "user_account_age_days":       312,
      "verified_phone":              true
    }
  }
}
Field Type Constraint Notes
intent string REQUIRED, equals "food.book_dine_in"
search_area.radius_km int REQUIRED, 1-25
scheduled_for_iso ISO_DATETIME REQUIRED
duration_minutes int REQUIRED, 30-300 typical 90-180
meal_period enum REQUIRED, see §5
party.adult_count int REQUIRED, ≥1
party.child_count int REQUIRED, ≥0
party.infant_count int REQUIRED, ≥0
party.high_chair_required bool REQUIRED
party.wheelchair_required bool REQUIRED
party.occasion enum REQUIRED, see §5
preferences.preferred_seating_kind array REQUIRED, ≥1 see §5
preferences.atmosphere_kinds_wanted array REQUIRED, ≥1 see §5
preferences.alcohol_served_acceptable bool REQUIRED drives bar-restaurant filter
preferences.music_kind_acceptable array REQUIRED, ≥1 see §5
preferences.valet_parking_required bool REQUIRED
preferences.kids_menu_required bool REQUIRED drives child-friendly filter
context.is_weekend bool REQUIRED
context.is_festival_day bool REQUIRED drives demand model

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


3. PROVIDER TOOLS

Tool 1: search_dine_in_options

PURPOSE:        return ranked list of restaurants with available slots
INPUT:          §2 request body
OUTPUT:         { results: DineInOption[], result_token, expires_at }
SLA:            p50 < 600ms, p95 < 1500ms
RATE LIMIT:     ≤ 1/sec per user

Tool 2: get_restaurant_detail

PURPOSE:        full detail (menu preview, photos, reviews, slots)
INPUT:          { restaurant_id, request_id, user_session_id }
OUTPUT:         RestaurantDetail (§4)
SLA:            p95 < 800ms

Tool 3: get_available_slots

PURPOSE:        live slot availability for a specific restaurant + date
INPUT:          { restaurant_id, date_iso, party_size, request_id }
OUTPUT:         { slots: SlotOption[], expires_at }
SLA:            p95 < 500ms

Tool 4: book_table

PURPOSE:        commit reservation
INPUT:          { restaurant_id, slot_id, party, payment_token, request_id, idempotency_key, user_phone, special_requests }
OUTPUT:         { reservation_ref, status, slot_iso, table_assigned, deposit_inr, cancellation_window_iso }
SLA:            p95 < 4000ms
IDEMPOTENCY:    REQUIRED on idempotency_key

Tool 5: modify_reservation

PURPOSE:        modify time / party size / special requests
INPUT:          { reservation_ref, new_slot_iso, new_party_size, new_special_requests, request_id }
OUTPUT:         { revised_status, revised_table_assigned, deposit_delta_inr }
SLA:            p95 < 2000ms

Tool 6: cancel_reservation

PURPOSE:        cancel
INPUT:          { reservation_ref, reason, request_id }
OUTPUT:         { status, refund_amount_inr, no_show_charge_inr, refund_processing_days }
SLA:            p95 < 2000ms

Tool 7: confirm_arrival

PURPOSE:        partner marks user arrived (or no-show)
INPUT:          { reservation_ref, status, observed_iso, request_id }
OUTPUT:         { acknowledged: true }
SLA:            p95 < 800ms

Tool 8: rate_dine_in

PURPOSE:        post-meal rating + tip
INPUT:          { reservation_ref, rating_5star, food_score, ambience_score, service_score, comment, tip_inr, request_id }
OUTPUT:         { acknowledged: true }
SLA:            p95 < 800ms

All eight REQUIRED.


4. RESPONSE SHAPE

DineInOption (returned by search_dine_in_options)

restaurant:                       Restaurant, REQUIRED
available_slots:                  array<SlotOption>, REQUIRED, ≥1
match_reason:                     STRICT ENUM, REQUIRED
distance_from_user_km:            float, REQUIRED
avg_check_inr_per_head:           INR_INTEGER, REQUIRED
sample_dishes:                    array<DishSummary>, REQUIRED, ≥3, ≤6   # menu preview

Restaurant

id:                               string, REQUIRED
merchant_id:                      string, REQUIRED
name:                             string, REQUIRED
official_name:                    string, REQUIRED
brand:                            string, REQUIRED
kind:                             STRICT ENUM, REQUIRED
sub_kind:                         STRICT ENUM, REQUIRED
cuisines:                         array<enum>, REQUIRED, ≥1
specialties:                      array<string>, REQUIRED, ≥0
veg_only_kitchen:                 boolean, REQUIRED
jain_meals_available:             boolean, REQUIRED
halal_meals_available:            boolean, REQUIRED
alcohol_served:                   boolean, REQUIRED
alcohol_license_kind:             STRICT ENUM, REQUIRED
vegan_options_count:              int, REQUIRED, ≥0
gluten_free_options_count:        int, REQUIRED, ≥0
total_dish_count:                 int, REQUIRED, ≥1

address:
  line_1:                         string, REQUIRED
  line_2:                         string, REQUIRED
  neighborhood:                   string, REQUIRED
  city:                           string, REQUIRED
  state:                          string, REQUIRED
  pincode:                        string, REQUIRED
  country_code:                   string, REQUIRED
  lat:                            float, REQUIRED
  lng:                            float, REQUIRED
  google_place_id:                string, REQUIRED
  landmark:                       string, REQUIRED

distance_from_user_km:            float, REQUIRED

ratings:
  overall_score:                  float, REQUIRED, 0-5
  overall_count:                  int, REQUIRED, ≥0
  recent_30day_score:             float, REQUIRED, 0-5
  recent_30day_count:             int, REQUIRED, ≥0
  food_quality_score:             float, REQUIRED, 0-5
  ambience_score:                 float, REQUIRED, 0-5
  service_score:                  float, REQUIRED, 0-5
  hygiene_score:                  float, REQUIRED, 0-5
  value_for_money_score:          float, REQUIRED, 0-5
  category_breakdown:             object, REQUIRED
    taste:                        float, REQUIRED, 0-5
    portion:                      float, REQUIRED, 0-5
    accuracy:                     float, REQUIRED, 0-5
    presentation:                 float, REQUIRED, 0-5

operating_hours:
  is_open_now:                    boolean, REQUIRED
  next_open_iso:                  ISO_DATETIME, REQUIRED
  next_close_iso:                 ISO_DATETIME, REQUIRED
  hours_of_operation:             array, REQUIRED, 7 entries
    - day_of_week:                STRICT ENUM, REQUIRED
      open_iso_time:              string, REQUIRED
      close_iso_time:             string, REQUIRED
      closed_today:               boolean, REQUIRED
      meal_periods_supported:     array<STRICT ENUM>, REQUIRED, may be empty

seating:
  total_capacity:                 int, REQUIRED, ≥1
  indoor_ac_seats:                int, REQUIRED, ≥0
  indoor_non_ac_seats:            int, REQUIRED, ≥0
  outdoor_garden_seats:           int, REQUIRED, ≥0
  outdoor_terrace_seats:          int, REQUIRED, ≥0
  private_dining_rooms:           int, REQUIRED, ≥0
  private_dining_max_capacity:    int, REQUIRED, ≥0
  bar_seats:                      int, REQUIRED, ≥0
  booth_seats:                    int, REQUIRED, ≥0
  rooftop_seats:                  int, REQUIRED, ≥0
  high_chair_count:               int, REQUIRED, ≥0
  wheelchair_accessible:          boolean, REQUIRED
  wheelchair_table_count:         int, REQUIRED, ≥0

atmosphere:
  noise_level:                    STRICT ENUM, REQUIRED
  lighting:                       STRICT ENUM, REQUIRED
  music_kind:                     STRICT ENUM, REQUIRED
  music_volume:                   STRICT ENUM, REQUIRED
  family_friendly:                boolean, REQUIRED
  romantic_friendly:              boolean, REQUIRED
  business_friendly:              boolean, REQUIRED
  pet_friendly:                   boolean, REQUIRED
  kid_friendly:                   boolean, REQUIRED
  has_kids_menu:                  boolean, REQUIRED
  has_kids_play_area:             boolean, REQUIRED
  smoking_zone_available:         boolean, REQUIRED
  smoking_outdoor_only:           boolean, REQUIRED

amenities:
  wifi_available:                 boolean, REQUIRED
  wifi_free:                      boolean, REQUIRED
  parking_kind:                   STRICT ENUM, REQUIRED
  valet_available:                boolean, REQUIRED
  valet_charge_inr:               INR_INTEGER, REQUIRED
  ev_charging_available:          boolean, REQUIRED
  air_conditioning:               boolean, REQUIRED
  power_backup:                   boolean, REQUIRED

trust:
  fssai_license_number:           string, REQUIRED
  fssai_license_valid_until:      ISO_DATE, REQUIRED
  fssai_grade:                    STRICT ENUM, REQUIRED
  cleanliness_audit_iso:          ISO_DATETIME, REQUIRED
  cleanliness_audit_score:        int, REQUIRED, 0-100
  partner_account_age_days:       int, REQUIRED, ≥0
  tomo_field_team_audited:        boolean, REQUIRED
  health_inspection_authority:    STRICT ENUM, REQUIRED
  food_handler_kyc_pct:           int, REQUIRED, 0-100
  alcohol_license_valid_until:    ISO_DATE, REQUIRED            # if alcohol_served=false, may be sentinel
  fire_safety_compliant:          boolean, REQUIRED
  cctv_in_dining_area:            boolean, REQUIRED
  female_friendly_certified:      boolean, REQUIRED              # partner-internal cert
  has_panic_button:               boolean, REQUIRED

photos:
  thumbnail_url:                  URL, REQUIRED
  hero_url:                       URL, REQUIRED
  ambience_photos_url:            URL, REQUIRED                  # JSON manifest of multiple photos
  menu_photos_url:                URL, REQUIRED
  exterior_photos_url:            URL, REQUIRED
  food_photos_url:                URL, REQUIRED
  ai_generated_photos:            boolean, REQUIRED               # MUST be false

freshness:
  data_last_synced_iso:           ISO_DATETIME, REQUIRED
  menu_last_updated_iso:          ISO_DATETIME, REQUIRED
  prices_last_updated_iso:        ISO_DATETIME, REQUIRED
  last_inspected_iso:             ISO_DATETIME, REQUIRED

_provider:
  name:                           string, REQUIRED
  tomo_partner_id:                string, REQUIRED
  partner_tier:                   STRICT ENUM, REQUIRED
  deep_link:                      URL, REQUIRED
  partner_restaurant_url:         URL, REQUIRED
  customer_support_phone:         string, REQUIRED
  customer_support_email:         string, REQUIRED
  customer_support_24x7:          boolean, REQUIRED
  in_app_chat_supported:          boolean, REQUIRED
  partner_dine_in_volume_30d:     int, REQUIRED, ≥0
  partner_no_show_rate_30d:       float, REQUIRED, 0-1

SlotOption

slot_id:                          string, REQUIRED
slot_iso:                         ISO_DATETIME, REQUIRED
duration_minutes:                 int, REQUIRED
seating_kind:                     STRICT ENUM, REQUIRED          # see §5
table_kind:                       STRICT ENUM, REQUIRED          # 2_seater | 4_seater | 6_seater | 8_seater | banquet | private_room
covers_max:                       int, REQUIRED, ≥1
covers_min:                       int, REQUIRED, ≥0
deposit_inr:                      INR_INTEGER, REQUIRED          # 0 if no deposit required
deposit_redeemable_against_meal:  boolean, REQUIRED
cover_charge_inr:                 INR_INTEGER, REQUIRED          # entry fee, applied even if no order; 0 typical
arrival_window_minutes:           int, REQUIRED                   # how late before table released
cancellation_until_iso:           ISO_DATETIME, REQUIRED
cancellation_charge_after_iso_inr: INR_INTEGER, REQUIRED
no_show_charge_inr:               INR_INTEGER, REQUIRED
buffet_or_a_la_carte:             STRICT ENUM, REQUIRED          # buffet | a_la_carte | both
buffet_price_per_head_inr:        INR_INTEGER, REQUIRED          # 0 if not buffet
slot_kind:                        STRICT ENUM, REQUIRED          # see §5
high_chair_available:             boolean, REQUIRED
wheelchair_accessible_table:      boolean, REQUIRED
near_window:                      boolean, REQUIRED
near_kitchen:                     boolean, REQUIRED
view_kind:                        STRICT ENUM, REQUIRED          # see §5
freshness_iso:                    ISO_DATETIME, REQUIRED

DishSummary (sample dishes for menu preview)

id:                               string, REQUIRED
name:                             string, REQUIRED
description:                      string, REQUIRED
price_inr:                        INR_INTEGER, REQUIRED
mrp_inr:                          INR_INTEGER, REQUIRED
photo_url:                        URL, REQUIRED
photo_authenticity_verified:      boolean, REQUIRED
photo_ai_generated:               boolean, REQUIRED               # MUST be false
veg_or_non_veg:                   STRICT ENUM, REQUIRED
spice_level:                      STRICT ENUM, REQUIRED
allergens_present:                array<enum>, REQUIRED
dietary_tags:                     array<enum>, REQUIRED
serves_count:                     int, REQUIRED, ≥1
calories_kcal:                    int, REQUIRED
oil_used:                         STRICT ENUM, REQUIRED
popularity_rank:                  int, REQUIRED
order_count_30day:                int, REQUIRED, ≥0
average_dish_rating:              float, REQUIRED, 0-5
recommended:                      boolean, REQUIRED

RestaurantDetail (returned by get_restaurant_detail)

Superset of Restaurant with full menu (sections + dishes), expanded reviews, all photos, and full slot availability for the next 7 days.

restaurant:                       Restaurant, REQUIRED
menu_sections:                    array<MenuSection>, REQUIRED, ≥1
slots_next_7_days:                array<SlotOption>, REQUIRED, ≥0
reviews:                          array, REQUIRED, ≥0
  - excerpt:                      string, REQUIRED
    score:                        float, REQUIRED, 0-5
    food_score:                   float, REQUIRED, 0-5
    ambience_score:               float, REQUIRED, 0-5
    service_score:                float, REQUIRED, 0-5
    visit_kind:                   STRICT ENUM, REQUIRED
    review_date:                  ISO_DATE, REQUIRED
    verified_visit:               boolean, REQUIRED
specialties_signature_dishes:     array<dish_id>, REQUIRED, ≥0

Reservation (returned by book_table)

reservation_ref:                  string, REQUIRED
status:                           STRICT ENUM, REQUIRED          # see §5
slot_iso:                         ISO_DATETIME, REQUIRED
duration_minutes:                 int, REQUIRED
table_assigned:                   string, REQUIRED                # "Table 14, indoor AC"
restaurant_address:               string, REQUIRED
restaurant_phone:                 string, REQUIRED
deposit_paid_inr:                 INR_INTEGER, REQUIRED
arrival_window_close_iso:         ISO_DATETIME, REQUIRED
cancellation_until_iso:           ISO_DATETIME, REQUIRED
qr_code_for_arrival:              URL, REQUIRED
support_phone:                    string, REQUIRED
support_email:                    string, REQUIRED

Forbidden fields

paid_placement_score | sponsored_rank | promotion_priority |
artificial_demand_text | fake_recent_reservation_text |
auto_inflate_rating | partner_paid_for_top_listing |
ai_generated_photos (must equal false) | hidden_cover_charge_inr |
fake_partner_no_show_rate_30d

5. CONTROLLED VOCABULARIES

Restaurant.kind

restaurant | bistro | cafe | bakery_cafe | bar | pub | rooftop_bar |
fine_dining | family_restaurant | qsr | dessert_parlor | hotel_restaurant

Restaurant.sub_kind

fine_dining | casual_dining | family_restaurant | fast_casual | quick_service |
buffet | a_la_carte | thali_house | meals_house | brewery | wine_bar |
sports_bar | rooftop | poolside | lounge_bar

cuisines

hyderabadi | south_indian | north_indian | bengali | punjabi | gujarati |
maharashtrian | rajasthani | kerala | tamilian | andhra | konkani | goan |
chinese | thai | korean | japanese | sushi | italian | french | continental |
mediterranean | mexican | lebanese | turkish | greek | mughlai | awadhi |
kashmiri | jain | vegan | live_grill | bbq | tandoor | chaat | street_food |
fusion | bowl | salad_bar | dessert | bakery | breakfast | brunch

meal_period

breakfast | brunch | lunch | tea_time | snacks | dinner | late_night | open_all_day

party.occasion

casual | date_night | anniversary | birthday | family_dinner |
friends_meetup | business_meeting | client_dinner | celebration |
proposal | engagement | reunion | farewell | other

dietary_filters

veg | non_veg | egg | vegan | jain | halal | kosher |
gluten_free | dairy_free | sugar_free | low_carb | low_sodium |
keto | paleo | high_protein | low_calorie | satvik

allergens_to_avoid

peanuts | tree_nuts | dairy | eggs | wheat | gluten | soy |
fish | shellfish | sesame | mustard | celery | lupin | sulphites

preferences.preferred_seating_kind and SlotOption.seating_kind

indoor_ac | indoor_non_ac | outdoor_garden | outdoor_terrace |
private_dining | bar_seating | booth | rooftop | poolside | window_seat |
chef_table | counter_seating

preferences.atmosphere_kinds_wanted

family_friendly | romantic | business | quiet | lively | rooftop_view |
poolside | garden | live_music | dj | sports_screen | pet_friendly |
kid_play_area | accessible

preferences.music_kind_acceptable and Restaurant.atmosphere.music_kind

no_music | soft_instrumental | lounge | jazz | classical | bollywood |
indie | live_band | dj | edm | hip_hop | acoustic | regional

Restaurant.atmosphere.noise_level

quiet | moderate | lively | loud

Restaurant.atmosphere.lighting

bright | warm | dim | candlelight | mixed

Restaurant.atmosphere.music_volume

none | low | moderate | high

Restaurant.amenities.parking_kind

valet_only | self_park_lot | street_parking | mall_parking |
no_parking | basement | rooftop_parking

Restaurant.alcohol_license_kind

none | bar_l_19 | restaurant_l_3 | wine_l_24 | beer_only | full_bar

SlotOption.seating_kind

Same enum as preferred_seating_kind.

SlotOption.table_kind

2_seater | 4_seater | 6_seater | 8_seater | banquet | private_room |
chef_table | rooftop_couch | window_table

SlotOption.buffet_or_a_la_carte

buffet | a_la_carte | both | tasting_menu | chef_special_only

SlotOption.slot_kind

walk_in_compatible | reserved_only | premium_slot | early_bird_slot |
late_dinner_slot | weekend_premium | event_special

SlotOption.view_kind

none | city | garden | pool | beach | mountain | sunset | sunrise |
courtyard | atrium | none_indoor

Reservation.status

confirmed | pending_partner_acceptance | pending_payment |
arrival_window_active | seated | meal_in_progress | completed |
cancelled_by_user | cancelled_by_restaurant | no_show | failed

Restaurant.trust.fssai_grade

A | B | C | unrated

Restaurant.trust.health_inspection_authority

fssai | local_municipality | none

RestaurantDetail.reviews[].visit_kind

date_night | family | friends | business | solo | celebration | other

dish.veg_or_non_veg

veg | non_veg | egg | vegan | jain

dish.spice_level

mild | medium | spicy | very_spicy

dish.oil_used

sunflower | mustard | groundnut | rice_bran | olive | coconut | ghee | butter | palm | mixed | none

cancel_reservation.reason

user_changed_mind | wrong_time | wrong_party_size | found_alternative |
medical_emergency | weather | partner_canceled_first | other

merchant_id resolution order

1. Google Place ID
2. FSSAI license number
3. partner_id + ":" + restaurant_id

6. TTBS DIMENSIONS

Per-domain weights (locked; dine-in override)

food (book_dine_in): { time: 0.20, taste: 0.40, budget: 0.20, safety: 0.20 }

Time drops (slot is scheduled, not ASAP). Taste rises (the experience IS the meal). Safety stays meaningful (food handling + physical).

TIME

SIGNALS USED:
  - slot_iso match request                           HARD FILTER (within ±30 min default)
  - distance_from_user_km                            weight 0.30
  - operating_hours.is_open_now                      HARD FILTER
  - SlotOption.arrival_window_minutes (longer=better) weight 0.20
  - eta to reach (driving) ≤ time_until_slot         HARD FILTER

USER BAND HANDLING:
  - "anytime tonight" → relax slot match window
  - "exactly 8pm" → tighten match

TASTE

SIGNALS USED:
  - ratings.food_quality_score                        weight 0.20
  - ratings.ambience_score                            weight 0.20
  - ratings.recent_30day_score                        weight 0.15
  - cuisines match user request                       weight 0.20
  - atmosphere.match (family_friendly, romantic etc)  weight 0.15
  - SlotOption.view_kind quality                      weight 0.10

HARD FILTERS:
  - dietary_filters / allergens
  - jain_meal_required, halal_required
  - alcohol_served_acceptable=false → drop alcohol-only bars
  - kids_menu_required AND has_kids_menu=false → drop

BUDGET

SIGNALS USED:
  - avg_check_inr_per_head vs band:
      ok    → 0–33rd percentile (cuisine, city)
      good  → 33rd–66th
      great → 66th+
  - SlotOption.deposit_inr (lower=better, all else equal) weight 0.20
  - SlotOption.cover_charge_inr (lower=better)        weight 0.20
  - amenities.valet_charge_inr (if valet required)    weight 0.15

HARD FILTERS:
  - avg_check > preferences.budget_max_inr_per_head → drop

SAFETY

SIGNALS USED:
  - trust.fssai_grade (A=high)                        weight 0.25
  - trust.cleanliness_audit_score                     weight 0.20
  - trust.fire_safety_compliant                       HARD FILTER
  - trust.alcohol_license_valid_until > slot_iso (if alcohol_served) HARD FILTER
  - trust.cctv_in_dining_area                         weight 0.10
  - trust.female_friendly_certified
      (for late dinner, female-led party)            weight 0.15
  - trust.has_panic_button                            weight 0.10
  - trust.tomo_field_team_audited                     weight 0.10
  - allergens overlap on dish level → drop dish (handled in Restaurant aggregation)
  - is_late_night_slot → safety scales 1.3x

HARD FILTERS:
  - jain_meal_required
  - halal_required
  - wheelchair_required → drop non-accessible

Hidden ranking factor

information_completeness_score weight 0.10.


7. COMPLETION CONTRACT

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

{
  "intent":            "food.book_dine_in",
  "intent_version":    "v1.0.0",
  "external_id":       "ZOMATO-RES-XYZ",
  "amount_inr":         3850,
  "closed_at":         "2026-05-10T22:14:00+05:30",
  "request_id":        "req_dn_8h2k_...",
  "status":            "completed",
  "reservation_ref":   "ZOMATO-RES-XYZ",
  "merchant_id":       "ChIJxxxxx",
  "restaurant_id":     "zomato_rest_8423",
  "seated_at":         "2026-05-10T20:32:00+05:30",
  "departed_at":       "2026-05-10T22:08:00+05:30",
  "promised_slot_iso": "2026-05-10T20:30:00+05:30",
  "actual_seat_iso":   "2026-05-10T20:32:00+05:30",
  "covers_seated":      4,
  "deposit_redeemed_inr": 500,
  "no_show_charge_inr":  0,
  "currency":          "INR",
  "fare_breakdown_total_inr": 3850,
  "rider_tip_inr":       0,
  "ratings_pending":     true,
  "notes":              ""
}

Status enum: completed | cancelled_by_user | cancelled_by_restaurant | no_show | failed | partial_completion_user_left_early


8. WIDGET

WIDGET TYPE:        dine_in_options
SOURCE:             src/widgets/types.ts
TYPE NAME:          DineInOptionsPayload
RENDERED IN:        components/widgets/DineInOptionsWidget.tsx

Default: 3 stacked rows showing restaurant name, cuisine, slot pill, avg-check-per-head, atmosphere badges. Tap row → restaurant detail card with menu preview, photos, slot grid, reviews, atmosphere/amenities → "Reserve". Then ReservationCard with QR code + arrival window + directions.


9. CACHING POLICY

Call TTL Rationale
search_dine_in_options 60s slot availability shifts
get_restaurant_detail 5min menu rarely changes
get_available_slots 30s live availability
book_table 0s
modify_reservation 0s
cancel_reservation 0s
confirm_arrival 0s
Failure responses 0s

10. ERROR CODES

Code HTTP Meaning TOMO behavior
NO_SLOTS_AVAILABLE 503 no slots match suggest alternates
RESTAURANT_CLOSED 409 restaurant closed for slot drop, refresh
OUT_OF_SERVICE_AREA 400 outside coverage surface
OPTION_EXPIRED 410 slot_id invalid re-quote
SLOT_JUST_TAKEN 409 slot booked by another user re-quote
MINIMUM_PARTY_SIZE 400 party below min surface
OVER_CAPACITY 400 party exceeds slot capacity surface alternate
DEPOSIT_REQUIRED 402 deposit not paid surface
ALCOHOL_LICENSE_EXPIRED 503 for alcohol restaurants surface
RESERVATION_NOT_FOUND 404 reservation_ref doesn't exist surface
ALREADY_CANCELLED 409 duplicate cancel idempotent return

11. SANDBOX → PRODUCTION CHECKLIST

[ ] All §2 inputs validated, request_id echoed
[ ] search_dine_in_options returns ≥10 restaurants with available slots
[ ] All §4 fields populated with REAL data
[ ] FSSAI license + grade present on every restaurant
[ ] alcohol_license valid for alcohol-serving establishments
[ ] photo_ai_generated == false everywhere
[ ] Allergens_present complete
[ ] Calorie macros on dishes per universal rule
[ ] book_table returns valid reservation_ref within SLA
[ ] modify_reservation handles slot + party changes correctly
[ ] cancel respects cancellation_until_iso
[ ] confirm_arrival fires within 5min of seat or no_show
[ ] CPC webhook within 60s of completion
[ ] HMAC verification passes
[ ] No forbidden fields anywhere
[ ] No paid placement / sponsored signals
[ ] customer_support_24x7 verified by TOMO field call
[ ] Fire safety + cleanliness audits uploaded
[ ] Female-friendly certification verified (if claimed)
[ ] Wheelchair accessibility tested in person

12. ANTI-FABRICATION RULES

RULE 1 — No paid placement signals.

RULE 2 — No artificial scarcity.
  "Only 1 slot left" requires real slot count. TOMO samples.

RULE 3 — Ratings unrounded floats from verified visits.

RULE 4 — No AI-generated photos. ai_generated_photos MUST be false.

RULE 5 — Deposit must be redeemable as advertised.
  deposit_redeemable_against_meal=true means user's bill is reduced by
  deposit_paid_inr. Charging full bill + keeping deposit = breach.

RULE 6 — No-show charges must match displayed.
  no_show_charge_inr cannot be inflated post-booking.

RULE 7 — Alcohol license claims verifiable.
  alcohol_served=true with expired license = severe compliance breach.

RULE 8 — FSSAI grade honest.
  Self-reporting A when actual is B = breach.

RULE 9 — No commission-based response shaping.

RULE 10 — Female-friendly certification real.
  female_friendly_certified=true requires partner-internal documented program.
  TOMO ops field-tests by sending female test ops at random.

RULE 11 — Allergens_present must be complete.
  Missing an allergen that turns out to be present = compliance breach.

RULE 12 — Slot honored or compensated.
  Promised slot must be honored within arrival_window_minutes. If restaurant
  cannot seat, partner pays driver_cancel_compensation equivalent.

RULE 13 — Capacity claims real.
  total_capacity must match physical reality. Inflating to attract
  larger-party bookings then refusing = breach.

VERSION HISTORY

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