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_catering_event

food.book_catering_event — Full Intent Specification

INTENT NAMESPACE: food
INTENT NAME:      book_catering_event
FULL ID:          food.book_catering_event
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

Catering is large-scale (50+ guests typical) event meal service: corporate offsites, weddings, festivals, conferences. Differs from chef-at-home (intimate, ≤15) and dine-in (no service-at-venue): partner brings full team + equipment + serving staff to the event venue. Locked structural shape: (a) event_kind array with multi-day support; (b) service_stations enum (live counter, buffet, plated, food truck, rooftop bar); (c) equipment_provided enum (chafing dishes, serving utensils, plates, cutlery, glassware); (d) staff_breakdown block (chefs, cooks, servers, supervisors); (e) decoration_responsibility enum.


1. NATURAL LANGUAGE COVERAGE

Classifies IN

  • "catering for wedding 200 guests"
  • "corporate offsite catering Friday for 80 people"
  • "festival catering for office 150 people"
  • "conference lunch catering"
  • "engagement reception catering 100 guests"
  • "birthday party catering 60 people"
  • "live counter catering for housewarming"
  • "buffet catering for college fest"
  • "food truck for kids party 50 kids"

Classifies OUT — borderline NO

  • "chef at home for dinner 6 people" → food.book_chef_at_home
  • "biryani delivery for office team of 12" → food.order_delivery
  • "table for 20 at restaurant" → food.book_dine_in
  • "tiffin for 30 employees" → food.subscribe_tiffin (recurring)

MULTI-INTENT TRIGGERS

  • "wedding catering and decoration vendor" → food.book_catering_event + (decoration not a TOMO intent)
  • "corporate catering and AV equipment rental" → food.book_catering_event + (rental not yet)
  • "festival catering and parking arrangements" → food.book_catering_event + mobility.book_chauffeur_hourly (corporate fleet)

2. INPUT — TOMO → PROVIDER

{
  "intent":          "food.book_catering_event",
  "intent_version":  "v1.0.0",
  "request_id":      "req_cat_8h2k_2026-05-10T08:00:00Z",
  "user_session_id": "anon_user_token_or_uid",

  "event_kind":              "wedding_reception",
  "event_name":              "Krishna-Anjali Reception",
  "event_kinds_required":    ["lunch_buffet", "evening_snacks", "dinner_buffet"],

  "venue": {
    "lat":            17.4504,
    "lng":            78.3811,
    "venue_name":     "Banjara Palace Banquet Hall",
    "address":        "Road No. 12, Banjara Hills",
    "neighborhood":   "Banjara Hills",
    "city":           "Hyderabad",
    "state_code":     "TS",
    "country_code":   "IN",
    "venue_kind":     "banquet_hall",
    "indoor_outdoor": "indoor",
    "kitchen_available_at_venue": false,
    "kitchen_kind_at_venue":      "none",
    "power_backup_available":     true,
    "water_supply_potable":       true,
    "max_capacity":               300,
    "access_notes":               "Loading dock at rear, freight lift to ground floor"
  },

  "schedule": {
    "is_multi_day":       false,
    "scheduled_start_iso": "2026-06-15T11:00:00+05:30",
    "scheduled_end_iso":   "2026-06-15T23:00:00+05:30",
    "service_legs": [
      {
        "leg_kind":        "lunch_buffet",
        "leg_start_iso":   "2026-06-15T12:00:00+05:30",
        "leg_end_iso":     "2026-06-15T15:00:00+05:30",
        "guest_count":     250
      },
      {
        "leg_kind":        "evening_snacks",
        "leg_start_iso":   "2026-06-15T17:00:00+05:30",
        "leg_end_iso":     "2026-06-15T19:00:00+05:30",
        "guest_count":     200
      },
      {
        "leg_kind":        "dinner_buffet",
        "leg_start_iso":   "2026-06-15T20:00:00+05:30",
        "leg_end_iso":     "2026-06-15T22:30:00+05:30",
        "guest_count":     250
      }
    ]
  },

  "guest_profile": {
    "max_guest_count":     250,
    "adult_count_max":     220,
    "child_count_max":     30,
    "infant_count_max":    0,
    "veg_count_estimate":  150,
    "non_veg_count_estimate": 100,
    "dietary_filters_aggregate": ["jain_count_5", "vegan_count_8", "halal_count_30"],
    "allergens_to_avoid_aggregate": ["peanuts", "tree_nuts"]
  },

  "menu_brief": {
    "cuisines_primary":      ["hyderabadi", "north_indian"],
    "cuisines_acceptable":   ["hyderabadi", "north_indian", "south_indian", "chinese"],
    "course_count_per_leg":  {"lunch_buffet": 12, "evening_snacks": 8, "dinner_buffet": 16},
    "live_counters_required": ["chaat", "live_dosa", "biryani_dum"],
    "alcohol_serving_required": true,
    "alcohol_kind":          "liquor_full_bar",
    "preferred_signature_dishes": ["Hyderabadi biryani", "Boondi raita", "Double ka meetha"],
    "avoid_dishes":          [],
    "kid_friendly_section_required": true
  },

  "service_kind_required": ["buffet_self_serve", "live_counter", "plated_first_round"],
  "decoration_responsibility": "user_provides",
  "equipment_responsibility": "partner_provides",
  "staff_count_minimum":      25,

  "preferences": {
    "budget_band":             "great",
    "budget_max_inr_total":    275000,
    "per_head_target_inr":     1100,
    "service_staff_attire":    "branded_uniform",
    "leftover_management_required": "donated_or_packed",
    "platform_fee_transparency_required": true,
    "fssai_grade_min":         "A",
    "kitchen_inspection_required_pre_event": true
  },

  "context": {
    "user_locale":         "en-IN",
    "user_currency_pref":  "INR",
    "is_festival_day":     false,
    "is_high_demand_season": true,
    "trust_signals": {
      "is_repeat_customer":          false,
      "prior_caterings_with_partner": 0,
      "user_account_age_days":       312,
      "verified_phone":              true
    }
  }
}
Field Type Constraint Notes
intent string REQUIRED, equals "food.book_catering_event"
event_kind enum REQUIRED, see §5
event_name string REQUIRED, may be empty
event_kinds_required array REQUIRED, ≥1 drives course count per leg
venue.venue_kind enum REQUIRED, see §5
venue.indoor_outdoor enum REQUIRED, STRICT indoor | outdoor | both
venue.kitchen_available_at_venue bool REQUIRED drives partner kitchen logistics
venue.kitchen_kind_at_venue enum REQUIRED, see §5
venue.max_capacity int REQUIRED, ≥1
schedule.is_multi_day bool REQUIRED
schedule.service_legs array REQUIRED, ≥1 each leg has start/end/guest count
schedule.service_legs[].leg_kind enum REQUIRED, see §5
guest_profile.max_guest_count int REQUIRED, ≥1
guest_profile.veg_count_estimate int REQUIRED, ≥0
guest_profile.non_veg_count_estimate int REQUIRED, ≥0
guest_profile.dietary_filters_aggregate array REQUIRED, may be empty format "tag_count_N"
menu_brief.cuisines_primary array REQUIRED, ≥1
menu_brief.course_count_per_leg object REQUIRED leg_kind → count map
menu_brief.live_counters_required array REQUIRED, may be empty see §5
menu_brief.alcohol_serving_required bool REQUIRED
menu_brief.alcohol_kind enum REQUIRED, see §5
menu_brief.kid_friendly_section_required bool REQUIRED
service_kind_required array REQUIRED, ≥1 see §5
decoration_responsibility enum REQUIRED, STRICT user_provides | partner_provides | third_party
equipment_responsibility enum REQUIRED, STRICT user_provides | partner_provides | third_party
staff_count_minimum int REQUIRED, ≥1
preferences.fssai_grade_min enum REQUIRED, STRICT A | B | C hard floor
preferences.kitchen_inspection_required_pre_event bool REQUIRED
preferences.leftover_management_required enum REQUIRED, STRICT donated_or_packed | partner_disposes | user_takes_home

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


3. PROVIDER TOOLS

Tool 1: get_catering_estimates

PURPOSE:        return catering options across partners
INPUT:          §2 request body
OUTPUT:         { options: CateringOption[], result_token, expires_at }
SLA:            p50 < 1500ms, p95 < 4000ms      (catering is compute-intensive)
RATE LIMIT:     ≤ 1/sec per user

Tool 2: get_partner_detail_with_menus

PURPOSE:        partner profile + sample menus + past event photos
INPUT:          { partner_id, request_id }
OUTPUT:         CateringPartnerDetail (§4)
SLA:            p95 < 1000ms

Tool 3: compute_event_quote

PURPOSE:        compute total for finalized menu + staff + equipment
INPUT:          { partner_id, menu_id, modifications, staff_overrides, request_id }
OUTPUT:         EventQuote (§4)
SLA:            p95 < 1000ms

Tool 4: book_catering_event

PURPOSE:        commit booking with deposit
INPUT:          { option_id, payment_token, request_id, idempotency_key, user_phone, otp_required, deposit_token }
OUTPUT:         { booking_ref, status, deposit_inr, balance_due_inr, balance_due_iso, partner_arrival_window_iso }
SLA:            p95 < 6000ms
IDEMPOTENCY:    REQUIRED on idempotency_key

Tool 5: get_event_state

PURPOSE:        live state through setup, service, teardown
INPUT:          { booking_ref, request_id }
OUTPUT:         CateringEventState (§4)
SLA:            p95 < 800ms
RATE LIMIT:     ≤ 1 every 60s during setup; ≤1/30s during service

Tool 6: cancel_catering_event

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

Tool 7: request_event_modification

PURPOSE:        modify guest count, add/remove leg, change menu items
INPUT:          { booking_ref, modifications[], request_id }
OUTPUT:         { status, fare_delta_inr, modifications_accepted, modifications_rejected }
SLA:            p95 < 2000ms

Tool 8: submit_pre_event_inspection

PURPOSE:        partner uploads pre-event kitchen + staff inspection report
INPUT:          { booking_ref, inspection_kit, request_id }
OUTPUT:         { acknowledged: true, inspection_iso, fssai_grade_observed }
SLA:            p95 < 2000ms

Tool 9: confirm_event_complete

PURPOSE:        record event completion + photos + leftovers
INPUT:          { booking_ref, end_photos[], guests_served_actual, leftovers_donated, kitchen_cleanup_score, request_id }
OUTPUT:         { acknowledged: true, completion_iso }
SLA:            p95 < 2000ms

Tool 10: rate_catering_event

INPUT:          { booking_ref, rating_5star, food_score, service_score, hygiene_score, presentation_score, comment, tip_inr, request_id }
OUTPUT:         { acknowledged: true }
SLA:            p95 < 800ms

All ten REQUIRED.


4. RESPONSE SHAPE

CateringOption

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

partner_kitchen:
  id:                             string, REQUIRED
  merchant_id:                    string, REQUIRED
  display_name:                   string, REQUIRED
  brand:                          string, REQUIRED
  partner_kitchen_kind:           STRICT ENUM, REQUIRED       # see §5
  cuisines_specialty:             array<enum>, REQUIRED, ≥1
  total_event_volume_30day:       int, REQUIRED, ≥0
  partner_account_age_days:       int, REQUIRED, ≥0
  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
    food_handler_kyc_pct:         int, REQUIRED, 0-100
    insurance_coverage_inr:       INR_INTEGER, REQUIRED
    food_poisoning_coverage:      boolean, REQUIRED
    fire_safety_at_kitchen:       boolean, REQUIRED
    cold_chain_compliance:        boolean, REQUIRED
    catering_council_member:      boolean, REQUIRED
    tomo_field_team_audited:      boolean, REQUIRED
  ratings:
    overall_score:                float, REQUIRED, 0-5
    overall_count:                int, REQUIRED, ≥0
    food_quality_score:           float, REQUIRED, 0-5
    service_score:                float, REQUIRED, 0-5
    presentation_score:           float, REQUIRED, 0-5
    hygiene_score:                float, REQUIRED, 0-5
    on_time_setup_30day_pct:      float, REQUIRED, 0-1

menu:
  menu_id:                        string, REQUIRED
  menu_name:                      string, REQUIRED
  legs:                           array, REQUIRED, ≥1
    - leg_kind:                   STRICT ENUM, REQUIRED
      courses:                    array, REQUIRED, ≥1
        - course_kind:            STRICT ENUM, REQUIRED
          dishes:                 array<DishSummary>, REQUIRED, ≥1
      live_counters:              array<STRICT ENUM>, REQUIRED, may be empty
      bar_kind:                   STRICT ENUM, REQUIRED        # see §5; "none" if no alcohol
  jain_section_supported:         boolean, REQUIRED
  vegan_section_supported:        boolean, REQUIRED
  halal_section_supported:        boolean, REQUIRED
  satvik_section_supported:       boolean, REQUIRED
  kid_friendly_section_supported: boolean, REQUIRED

staff_breakdown:
  total_staff:                    int, REQUIRED
  chefs_count:                    int, REQUIRED
  cooks_count:                    int, REQUIRED
  servers_count:                  int, REQUIRED
  supervisors_count:              int, REQUIRED
  bartenders_count:               int, REQUIRED                # 0 if no bar
  serving_staff_attire:           STRICT ENUM, REQUIRED        # see §5
  staff_kyc_pct:                  int, REQUIRED, 0-100
  staff_uniform_compliance_pct:   int, REQUIRED, 0-100

equipment_provided:
  chafing_dishes_count:           int, REQUIRED
  serving_utensils_count:         int, REQUIRED
  plates_count:                   int, REQUIRED
  cutlery_sets_count:             int, REQUIRED
  glassware_count:                int, REQUIRED
  bar_setup_provided:             boolean, REQUIRED
  live_counter_stations_count:    int, REQUIRED
  food_warmers_count:             int, REQUIRED
  refrigeration_at_venue:         boolean, REQUIRED            # for cold-chain items
  power_backup_at_venue_provided: boolean, REQUIRED            # if user's venue lacks
  decoration_supplied_in_kit:     boolean, REQUIRED            # basic centerpiece, etc.

logistics:
  partner_arrival_iso:            ISO_DATETIME, REQUIRED        # how early before scheduled_start
  setup_minutes_required:         int, REQUIRED
  teardown_minutes_required:      int, REQUIRED
  truck_count:                    int, REQUIRED, ≥1
  alcohol_license_at_event_held:  boolean, REQUIRED
  alcohol_license_kind:           STRICT ENUM, REQUIRED        # see §5
  alcohol_license_valid_until:    ISO_DATE, REQUIRED
  ice_supply_in_kit:              boolean, REQUIRED
  cold_storage_truck_provided:    boolean, REQUIRED

fare:
  total_inr:                      INR_INTEGER, REQUIRED
  per_head_inr:                   INR_INTEGER, REQUIRED
  food_cost_inr:                  INR_INTEGER, REQUIRED
  staff_cost_inr:                 INR_INTEGER, REQUIRED
  equipment_rental_inr:           INR_INTEGER, REQUIRED
  logistics_inr:                  INR_INTEGER, REQUIRED
  alcohol_charge_inr:             INR_INTEGER, REQUIRED
  service_charge_inr:             INR_INTEGER, REQUIRED
  decoration_inr:                 INR_INTEGER, REQUIRED        # 0 if user_provides
  platform_fee_inr:               INR_INTEGER, REQUIRED
  gst_inr:                        INR_INTEGER, REQUIRED
  fare_breakdown_text:            string, REQUIRED
  is_upfront_fare:                boolean, REQUIRED
  fare_locked_until_iso:          ISO_DATETIME, REQUIRED

deposit:
  required:                       boolean, REQUIRED
  amount_inr:                     INR_INTEGER, REQUIRED        # typically 30-50% of total
  deposit_pct:                    float, REQUIRED, 0-1
  hold_kind:                      STRICT ENUM, REQUIRED
  balance_due_iso:                ISO_DATETIME, REQUIRED        # typically 7 days before event

cancellation:
  free_cancel_until_iso:          ISO_DATETIME, REQUIRED
  cancel_charge_within_30days_inr: INR_INTEGER, REQUIRED
  cancel_charge_within_7days_inr: INR_INTEGER, REQUIRED
  cancel_charge_within_24h_inr:   INR_INTEGER, REQUIRED
  no_show_charge_inr:             INR_INTEGER, REQUIRED        # rare for catering
  partner_cancel_compensation_inr: INR_INTEGER, REQUIRED
  refund_processing_days:         int, REQUIRED, ≥0

freshness:
  data_last_synced_iso:           ISO_DATETIME, REQUIRED
  menu_last_updated_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
  partner_catering_volume_30d:    int, REQUIRED, ≥0
  partner_event_completion_rate_30d: float, REQUIRED, 0-1
  partner_avg_event_size_30d:     int, REQUIRED, ≥0

DishSummary

(Same structure as food.order_delivery §5 DishSummary. All fields REQUIRED.)

CateringPartnerDetail (returned by get_partner_detail_with_menus)

Superset of partner_kitchen block plus 5+ menu templates per cuisine, 20+ past event photos with consent-given user reviews, kitchen tour video URL.

EventQuote (returned by compute_event_quote)

partner_id:                       string, REQUIRED
menu_id:                          string, REQUIRED
modifications_count:              int, REQUIRED, ≥0
guest_count_locked:               int, REQUIRED
total_inr:                        INR_INTEGER, REQUIRED
per_head_inr:                     INR_INTEGER, REQUIRED
fare_breakdown_text:              string, REQUIRED
quote_valid_until_iso:            ISO_DATETIME, REQUIRED
modifications_accepted:           array, REQUIRED, may be empty
modifications_rejected:           array, REQUIRED, may be empty
guest_count_overage_per_head_inr: INR_INTEGER, REQUIRED        # if guest count exceeds locked count

CateringEventState

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

setup_progress:
  partner_arrived_iso:            ISO_DATETIME, REQUIRED        # may equal future iso
  setup_pct:                      int, REQUIRED, 0-100
  pre_event_inspection_complete:  boolean, REQUIRED
  inspection_grade_observed:      STRICT ENUM, REQUIRED
  staff_count_observed:           int, REQUIRED, ≥0

service_progress:
  current_leg_index:              int, REQUIRED, ≥-1            # -1 between legs
  guests_served_so_far:           int, REQUIRED, ≥0
  food_running_short_warning:     boolean, REQUIRED
  food_quality_observation_score: int, REQUIRED, 0-100          # partner self-report
  any_food_safety_incident:       boolean, REQUIRED

teardown_progress:
  teardown_started_iso:           ISO_DATETIME, REQUIRED        # may equal future
  cleanup_pct:                    int, REQUIRED, 0-100
  leftovers_packed:               boolean, REQUIRED
  leftovers_donated:              boolean, REQUIRED
  donation_recipient_name:        string, REQUIRED              # may be empty

support_phone:                    string, REQUIRED
support_email:                    string, REQUIRED

Forbidden fields

paid_placement_score | sponsored_rank | promotion_priority |
fake_recent_event_text | auto_inflate_event_volume |
ai_generated_event_photo (must equal false) | hidden_logistics_fee |
fake_alcohol_license | undisclosed_food_safety_incident |
fake_partner_event_completion_rate_30d

5. CONTROLLED VOCABULARIES

event_kind

wedding | wedding_reception | engagement | sangeet | mehendi |
corporate_offsite | corporate_lunch | conference | gala_dinner |
festival_meal | religious_event_meal | birthday | anniversary |
housewarming | college_fest | school_event | charity_event |
family_reunion | community_meal | other

event_kinds_required[] and service_legs[].leg_kind

breakfast_buffet | lunch_buffet | high_tea | evening_snacks |
dinner_buffet | late_night_supper | welcome_drink_session |
cocktail_evening | dessert_only | tasting_menu_plated | brunch

venue.venue_kind

banquet_hall | hotel_ballroom | resort | farmhouse | community_hall |
office | open_lawn | rooftop | beach | boat_houseboat |
industrial_warehouse | private_residence | event_park | other

venue.indoor_outdoor

indoor | outdoor | both

venue.kitchen_kind_at_venue

none | basic_warming | full_commercial | partner_kitchen_set_up_onsite |
limited_prep_only

partner_kitchen.partner_kitchen_kind

catering_company | hotel_catering | wedding_specialist |
corporate_catering | community_kitchen_co_op | celebrity_chef_catering |
food_truck_catering | mobile_kitchen

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 | satvik | live_grill | bbq | tandoor | chaat | fusion |
multi_cuisine_buffet

course_kind

welcome_drink | appetizer | starter | soup | salad | bread |
main | side | rice | biryani | pasta_or_noodle | dessert |
beverage | tea_coffee | dessert_buffet | mithai_section

live_counters_required[] and live_counters[]

chaat | live_dosa | live_pasta | live_pizza | live_grill | tandoor_live |
biryani_dum | live_chinese_wok | live_pasta | mocktail_counter |
juice_bar | salad_bar | sushi_bar | live_kebab | tava_paratha |
ice_cream_counter | dessert_counter | paan_counter | filter_coffee_counter

bar_kind and menu_brief.alcohol_kind

none | beer_only | wine_only | beer_wine | liquor_full_bar |
premium_full_bar | bar_specialty_cocktails

service_kind_required[]

buffet_self_serve | live_counter | plated_first_round | family_style |
food_truck | rooftop_bar | passed_appetizers | tasting_menu

staff_breakdown.serving_staff_attire

branded_uniform | chef_whites | smart_casual | traditional_indian |
formal_wear | event_specific_costume

partner_kitchen.trust.fssai_grade

A | B | C | unrated

dietary_filters_aggregate[] (special format)

veg_count_N | non_veg_count_N | egg_count_N | vegan_count_N |
jain_count_N | halal_count_N | satvik_count_N |
gluten_free_count_N | low_carb_count_N

allergens_to_avoid_aggregate[]

peanuts | tree_nuts | dairy | eggs | wheat | gluten | soy |
fish | shellfish | sesame

equipment_responsibility and decoration_responsibility

user_provides | partner_provides | third_party

logistics.alcohol_license_kind

none | event_l_19 | bar_l_19 | restaurant_l_3 | wine_l_24 |
beer_only | full_bar | one_day_event_permit

deposit.hold_kind

card_block | bank_transfer | wallet_hold | partial_advance | full_advance

CateringEventState.status

booked | confirmed | partner_assigned | inspection_pending |
inspection_complete | partner_en_route | partner_arrived |
setup_in_progress | service_leg_1 | between_legs |
service_leg_n | event_complete_teardown | teardown_complete |
fully_complete | cancelled_by_user | cancelled_by_partner |
incident_open | failed

CateringEventState.setup_progress.inspection_grade_observed

A | B | C | unrated

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_catering_event.reason

user_changed_plans | postponed_event | cancelled_event | venue_unavailable |
weather_block | budget_changed | found_alternative | medical | other

preferences.leftover_management_required

donated_or_packed | partner_disposes | user_takes_home

merchant_id resolution order

1. FSSAI license number
2. partner_id + ":" + kitchen_id
3. Google Place ID (if partner has fixed kitchen)

6. TTBS DIMENSIONS

Per-domain weights

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

TIME

SIGNALS USED:
  - logistics.partner_arrival_iso ≤ scheduled_start - setup_minutes_required HARD FILTER
  - partner_kitchen.ratings.on_time_setup_30day_pct       weight 0.40
  - logistics.setup_minutes_required (lower=better)        weight 0.20
  - cancellation.refund_processing_days                    weight 0.10
  - service_legs scheduling feasibility validated          HARD FILTER

TASTE

SIGNALS USED:
  - partner_kitchen.ratings.food_quality_score             weight 0.20
  - partner_kitchen.ratings.presentation_score             weight 0.20
  - partner_kitchen.ratings.recent_30day_score             weight 0.15
  - menu cuisine match user.cuisines_primary               weight 0.20
  - menu.live_counters_required all supported              HARD FILTER (or partial filter)
  - menu jain/halal/vegan sections support user dietary    HARD FILTER

HARD FILTERS:
  - jain_section needed AND jain_section_supported=false
  - halal_section needed AND halal_section_supported=false
  - kid_friendly_section_required AND kid_friendly_section_supported=false
  - alcohol_serving_required AND alcohol_license_at_event_held=false
  - allergens_to_avoid present in menu

BUDGET

SIGNALS USED:
  - per_head_inr vs band:
      ok    → 0–33rd percentile (cuisine, city, event_kind)
      good  → 33rd–66th
      great → 66th+
  - fare.is_upfront_fare=true                              weight 0.20
  - guest_count_overage_per_head_inr (lower=better)        weight 0.20
  - deposit.deposit_pct (lower=better, all else equal)     weight 0.15
  - cancellation tier permissiveness                       weight 0.15
  - decoration_inr included if responsibility=partner      weight 0.10
  - platform_fee_inr (lower=better)                        weight 0.10

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

SAFETY

SIGNALS USED:
  - partner_kitchen.trust.fssai_grade ≥ preferences.fssai_grade_min HARD FILTER
  - partner_kitchen.trust.cleanliness_audit_score          weight 0.25
  - partner_kitchen.trust.food_handler_kyc_pct (≥80)       weight 0.15
  - partner_kitchen.trust.fire_safety_at_kitchen           HARD FILTER
  - partner_kitchen.trust.cold_chain_compliance            weight 0.15
  - partner_kitchen.trust.food_poisoning_coverage          weight 0.20
  - partner_kitchen.trust.insurance_coverage_inr (≥1000K large events) weight 0.15
  - partner_kitchen.trust.tomo_field_team_audited          weight 0.10
  - staff_breakdown.staff_kyc_pct (≥90)                    weight 0.10
  - staff_breakdown.staff_uniform_compliance_pct (≥90)     weight 0.05
  - menu_brief.alcohol_serving_required AND alcohol_license_kind invalid HARD FILTER
  - is_outdoor_venue → safety scales 1.2x (weather/sanitation risk)

HARD FILTERS:
  - kitchen_inspection_required_pre_event AND partner doesn't support → drop
  - alcohol_serving_required AND alcohol license missing → drop

Hidden ranking factor

information_completeness_score weight 0.10.


7. COMPLETION CONTRACT

POST /api/v1/cpc/mcp_provider/{tomo_partner_id}
{
  "intent":            "food.book_catering_event",
  "intent_version":    "v1.0.0",
  "external_id":       "CATER-XYZ",
  "amount_inr":         268000,
  "closed_at":         "2026-06-15T23:30:00+05:30",
  "request_id":        "req_cat_8h2k_...",
  "status":            "completed",
  "booking_ref":       "CATER-XYZ",
  "event_started_at":  "2026-06-15T11:00:00+05:30",
  "event_completed_at": "2026-06-15T22:30:00+05:30",
  "guests_served_actual": 248,
  "guest_count_overage_inr": 0,
  "service_legs_completed": 3,
  "any_food_safety_incident": false,
  "leftovers_donated":  true,
  "donation_recipient_name": "Robin Hood Army Hyderabad",
  "kitchen_cleanup_score": 95,
  "currency":          "INR",
  "fare_breakdown_total_inr": 268000,
  "rider_tip_inr":       5000,
  "ratings_pending":     true,
  "notes":              ""
}

Status enum: completed | cancelled_by_user | cancelled_by_partner | failed | partial_completion_event_cut_short | terminated_with_food_safety_incident | terminated_with_alcohol_license_violation


8. WIDGET

WIDGET TYPE:        catering_event_options
SOURCE:             src/widgets/types.ts
TYPE NAME:          CateringEventOptionsPayload
RENDERED IN:        components/widgets/CateringEventOptionsWidget.tsx

Default: 3 stacked rows showing partner name, cuisine specialty, per-head price, FSSAI grade badge, on-time-setup-rate. Tap row → partner detail with menu carousel + past event photos + reviews → "Book event". Then CateringDashboard with setup progress + service leg timeline + teardown.


9. CACHING POLICY

Call TTL Rationale
get_catering_estimates 600s catering quotes change slowly
get_partner_detail_with_menus 5min menus rarely shift
compute_event_quote 5min quote_valid_until_iso enforced
book_catering_event 0s
get_event_state 30s live during event
Failure responses 0s

10. ERROR CODES

Code HTTP Meaning TOMO behavior
NO_PARTNERS_AVAILABLE 503 no partner matches retry or fall back
OUT_OF_SERVICE_AREA 400 venue outside coverage surface
OPTION_EXPIRED 410 option_token invalid re-quote
MENU_INFEASIBLE 400 menu impossible at venue surface
ALCOHOL_LICENSE_INVALID 503 license expired or wrong kind surface alternate
BOOKING_NOT_FOUND 404 booking_ref doesn't exist surface
ALREADY_CANCELLED 409 duplicate cancel idempotent
INSPECTION_FAILED 503 pre-event inspection found violations surface decision flow
STAFF_KYC_INSUFFICIENT 503 staff KYC % below floor surface alternate
GUEST_COUNT_EXCEEDS_VENUE_CAPACITY 400 venue.max_capacity exceeded surface
INGREDIENT_LOGISTICS_FAILED 503 partner cannot source ingredients surface

11. SANDBOX → PRODUCTION CHECKLIST

[ ] All §2 inputs validated, request_id echoed
[ ] get_catering_estimates returns ≥3 options for "wedding 200 guests Hyderabad"
[ ] All §4 fields populated with REAL data
[ ] FSSAI license + grade present + valid through event date
[ ] Insurance coverage ≥1000K verified for large events
[ ] alcohol_license_at_event_held tested if alcohol_serving_required
[ ] photo_ai_generated == false everywhere
[ ] book_catering_event returns booking_ref + balance_due schedule
[ ] Pre-event inspection flow tested with photo upload
[ ] get_event_state returns setup_pct in real time
[ ] cancel respects cancellation tiers (30day, 7day, 24h)
[ ] CPC webhook arrives within 60s of completion
[ ] HMAC verification passes
[ ] No forbidden fields anywhere
[ ] No paid placement / sponsored signals
[ ] customer_support_24x7 verified by TOMO field call
[ ] Insurance certificate uploaded for liability claims
[ ] Staff KYC artifacts uploaded for sample staff
[ ] Sample menu photos verified non-AI
[ ] Catering council membership verified (if claimed)

12. ANTI-FABRICATION RULES

RULE 1 — No paid placement signals.

RULE 2 — Ratings unrounded floats from verified events only.

RULE 3 — No fake event volume or completion rate inflation.
  TOMO compares stated vs partner CPC ledger; >5% mismatch = breach.

RULE 4 — No AI-generated event photos.

RULE 5 — Alcohol license must be valid AT event date.
  Booking with license expired before scheduled_end_iso = severe compliance breach.

RULE 6 — FSSAI grade real and current.
  Grade A claim must match latest published FSSAI inspection. Self-elevating
  to attract premium events = breach.

RULE 7 — Insurance coverage real.

RULE 8 — Food poisoning coverage actually pays.
  food_poisoning_coverage=true must trigger partner liability + medical
  reimbursement. Suppressing or refusing claims = severe breach.

RULE 9 — No commission-based response shaping.

RULE 10 — Pre-event inspection cannot be skipped if required.
  kitchen_inspection_required_pre_event=true means partner submits inspection
  ≥48h before event. Skipping = breach.

RULE 11 — Staff count honored.
  staff_count delivered must match contracted count. Showing up with 18
  when 25 was contracted = breach + automatic refund tier.

RULE 12 — No fake ingredients sourcing.
  Partner cannot substitute "fresh paneer" with cottage cheese mix without
  user notification. Substitution requires user consent.

RULE 13 — Leftover management honored.
  leftover_management_required=donated_or_packed means leftovers actually
  reach a verifiable food bank or are packed for user. Disposing leftovers
  silently = breach.

RULE 14 — Food safety incidents must be reported.
  Suppressing reported food poisoning post-event to preserve completion
  rate = severe breach + criminal exposure.

RULE 15 — Equipment provided as advertised.
  equipment_provided counts must match physical reality. Showing up with
  10 chafing dishes when 18 contracted = breach.

RULE 16 — No bait-and-switch on chefs.
  Headline chef in marketing must actually attend the event for ≥50% of
  service hours. Substituting silently = breach.

VERSION HISTORY

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