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.
● LIVEv1.0.0food.order_delivery

food.order_delivery — Full Intent Specification

INTENT NAMESPACE: food
INTENT NAME:      order_delivery
FULL ID:          food.order_delivery
VERSION:          v1.0.0
STATUS:           live
LAST UPDATED:     2026-05-09
TTBS WEIGHTS:     time 0.30 · taste 0.35 · budget 0.20 · safety 0.15

1. NATURAL LANGUAGE COVERAGE

Classifies IN

  • "biryani for dinner"
  • "order pizza"
  • "swiggy / zomato"
  • "I'm hungry, get me something quick"
  • "South Indian breakfast tomorrow morning"
  • "paneer butter masala and naan"
  • "surprise me with something light tonight"
  • "order from Paradise biryani"
  • "tiffin for 2 people, no onion no garlic"

Classifies OUT — borderline NO

  • "table for 4 at Paradise" → food.book_dine_in
  • "groceries for the week" → grocery.order_delivery
  • "tiffin subscription for a month" → food.subscribe_tiffin
  • "cake for my daughter's birthday" → food.order_cake_or_special
  • "chef at home for a dinner party" → food.book_chef_at_home

MULTI-INTENT TRIGGERS

  • "biryani and a couple of cokes" → food.order_delivery (single composite cart)
  • "biryani for tonight, breakfast booked for tomorrow" → food.order_delivery + food.subscribe_tiffin
  • "I'm in Goa for the weekend, find a hotel and order food" → travel.book_hotel + food.order_delivery

2. INPUT — TOMO → PROVIDER

{
  "intent":          "food.order_delivery",
  "intent_version":  "v1.0.0",
  "request_id":      "req_8m4p9t_2026-05-09T19:32:00Z",
  "user_session_id": "anon_user_token_or_uid",

  "delivery_address": {
    "lat":            17.4504,
    "lng":            78.3811,
    "line_1":         "Flat 502, SriHomes Apartment",
    "line_2":         "Hitech City Main Road",
    "neighborhood":   "Madhapur",
    "city":           "Hyderabad",
    "state":          "Telangana",
    "pincode":        "500081",
    "country_code":   "IN",
    "delivery_notes": "Gate code 4521, leave at door"
  },

  "intent_kind":         "scheduled",
  "asap":                false,
  "scheduled_for_iso":   "2026-05-09T20:30:00+05:30",

  "party": {
    "diner_count":     2,
    "occasion":        "casual_dinner",
    "dietary_filters":     ["veg"],
    "allergens_to_avoid":  ["peanuts"]
  },

  "preferences": {
    "budget_band":                   "good",
    "budget_max_inr_total":           800,
    "cuisines_wanted":                ["hyderabadi", "south_indian"],
    "max_eta_minutes":                45,
    "spice_level_max":                "medium",
    "no_onion_no_garlic":             false,
    "jain_meal_required":             false,
    "halal_required":                 false,
    "single_use_plastic_free":        false,
    "preferred_packaging":            "biodegradable_or_recyclable"
  },

  "context": {
    "user_locale":        "en-IN",
    "user_currency_pref": "INR",
    "user_weather_celsius": 32,
    "user_time_local_iso":  "2026-05-09T19:32:00+05:30",
    "trust_signals": {
      "is_repeat_customer":          false,
      "prior_orders_with_partner":   0,
      "user_account_age_days":       312,
      "verified_address":            true
    }
  }
}
Field Type Constraint Notes
intent string REQUIRED, equals "food.order_delivery"
intent_kind enum REQUIRED, STRICT ENUM asap | scheduled
asap bool REQUIRED redundant with intent_kind for fast clients
scheduled_for_iso ISO_DATETIME REQUIRED if asap=true, set to now+15min sentinel
delivery_address.country_code ISO_3166_2 REQUIRED, always IN
delivery_address.delivery_notes string REQUIRED, may be empty
party.diner_count int REQUIRED, ≥1
party.occasion enum REQUIRED, STRICT ENUM see §6
party.dietary_filters array REQUIRED, may be empty see §6
party.allergens_to_avoid array REQUIRED, may be empty see §6
preferences.budget_band enum REQUIRED, STRICT ok | good | great
preferences.budget_max_inr_total INR_INTEGER REQUIRED hard ceiling
preferences.cuisines_wanted array REQUIRED, may be empty see §6
preferences.max_eta_minutes int REQUIRED, 5-180 hard filter
preferences.spice_level_max enum REQUIRED, STRICT ENUM see §6
preferences.no_onion_no_garlic bool REQUIRED hard filter
preferences.jain_meal_required bool REQUIRED hard filter
preferences.halal_required bool REQUIRED hard filter
preferences.single_use_plastic_free bool REQUIRED hard filter
preferences.preferred_packaging enum REQUIRED, STRICT ENUM see §6
context.user_weather_celsius float REQUIRED influences hot-vs-cold rec
context.user_time_local_iso ISO_DATETIME REQUIRED for breakfast/lunch/dinner classification
context.trust_signals.verified_address bool REQUIRED TOMO has verified pin lat/lng

Anti-fabrication preamble (universal): TOMO will never inject paid_placement, urgency, or commission-influenced fields. Provider may not return ad-promoted dishes/restaurants.


3. PROVIDER TOOLS

Tool 1: search_restaurants_and_dishes

PURPOSE:        return ranked list of restaurants matching delivery + filters
INPUT:          §2 request body
OUTPUT:         { results: SearchResult[], result_token, expires_at }
SLA:            p50 < 600ms, p95 < 1500ms, p99 < 3000ms
RATE LIMIT:     ≤ 1/sec per (user_session_id, partner)
RETRY:          retry once on 5xx with 1s/4s backoff
RESULT SET:     up to 50 SearchResults (each = restaurant with up to 5 surfaced dishes)

Tool 2: get_restaurant_menu

PURPOSE:        full menu for one restaurant
INPUT:          { restaurant_id, request_id, user_session_id, delivery_address }
OUTPUT:         RestaurantMenu (§5)
SLA:            p95 < 800ms
INVALIDATION:   restaurant_id from search expires at expires_at

Tool 3: get_dish_detail

PURPOSE:        full detail (incl. customizations, photos, allergen profile) for one dish
INPUT:          { restaurant_id, dish_id, request_id }
OUTPUT:         DishDetail (§5)
SLA:            p95 < 500ms

Tool 4: compute_cart_total

PURPOSE:        resolve a multi-item cart to a final total incl all fees
INPUT:          { restaurant_id, items[], delivery_address, scheduled_for_iso, request_id }
OUTPUT:         CartQuote (§5)
SLA:            p95 < 600ms
USE:            called whenever user adds/removes an item; pricing must be authoritative

Tool 5: place_order

PURPOSE:        commit the order
INPUT:          { restaurant_id, items[], delivery_address, payment_token,
                  scheduled_for_iso, request_id, idempotency_key, user_phone }
OUTPUT:         { order_ref, status, estimated_delivery_iso,
                  total_amount_inr, support_phone, support_email,
                  rider_assignment_eta_minutes }
SLA:            p95 < 5000ms
IDEMPOTENCY:    REQUIRED on idempotency_key
RETRY:          TOMO never retries automatically (user-driven)

Tool 6: cancel_order

PURPOSE:        cancel
INPUT:          { order_ref, reason, request_id }
OUTPUT:         { status, refund_amount_inr, refund_eta_days }
SLA:            p95 < 3000ms

Tool 7: track_order

PURPOSE:        live status + rider location
INPUT:          { order_ref, request_id }
OUTPUT:         OrderTrack (§5)
SLA:            p95 < 400ms
RATE LIMIT:     ≤ 1 every 10s per order

All seven tools are REQUIRED. No partial implementations.


4. RESPONSE SHAPE

SearchResult (returned by search_restaurants_and_dishes)

restaurant:                       Restaurant, REQUIRED        # see Restaurant below
surfaced_dishes:                  array<DishSummary>, REQUIRED, ≥1, ≤5
match_reason:                     STRICT ENUM, REQUIRED         # see §6
estimated_delivery_eta_minutes:   int, REQUIRED, ≥0
estimated_pickup_minutes:         int, REQUIRED, ≥0
estimated_cook_time_minutes:      int, REQUIRED, ≥0
delivery_fee_inr:                 INR_INTEGER, REQUIRED
distance_from_user_km:            float, REQUIRED

Restaurant

id:                               string, REQUIRED
merchant_id:                      string, REQUIRED
name:                             string, REQUIRED
official_name:                    string, REQUIRED
brand:                            string, REQUIRED
kind:                             STRICT ENUM, REQUIRED          # see §6
sub_kind:                         STRICT ENUM, REQUIRED          # see §6

cuisines:                         array<enum>, REQUIRED, ≥1
specialties:                      array<string>, REQUIRED, ≥0     # human-readable famous-for items
veg_only_kitchen:                 boolean, REQUIRED
jain_meals_available:             boolean, REQUIRED
halal_meals_available:            boolean, 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, ISO_3166_2
  lat:                            float, REQUIRED
  lng:                            float, REQUIRED
  google_place_id:                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
  packaging_score:                float, REQUIRED, 0-5
  delivery_time_score:            float, REQUIRED, 0-5
  delivery_partner_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          # may equal "now" if open
  next_close_iso:                 ISO_DATETIME, REQUIRED
  hours_of_operation:             array, REQUIRED, 7 entries
    - day_of_week:                STRICT ENUM, REQUIRED
      open_iso_time:              string, REQUIRED                # "08:00"
      close_iso_time:             string, REQUIRED                # "23:30"
      closed_today:               boolean, REQUIRED

eta:
  cook_minutes:                   int, REQUIRED
  pickup_minutes:                 int, REQUIRED
  delivery_minutes:               int, REQUIRED
  total_eta_minutes:              int, REQUIRED                  # cook+pickup+delivery
  current_capacity_pct:           int, REQUIRED, 0-100            # 100 = swamped

trust:
  fssai_license_number:           string, REQUIRED
  fssai_license_valid_until:      ISO_DATE, REQUIRED
  fssai_grade:                    STRICT ENUM, REQUIRED           # A | B | C | unrated
  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          # see §6
  food_handler_kyc_pct:           int, REQUIRED, 0-100

packaging:
  primary_material:               STRICT ENUM, REQUIRED          # see §6
  uses_single_use_plastic:        boolean, REQUIRED
  biodegradable:                  boolean, REQUIRED
  insulation:                     STRICT ENUM, REQUIRED          # none | thermal | insulated_bag | rigid_box
  spill_proof_seal:               boolean, REQUIRED
  cutlery_provided:               boolean, REQUIRED
  cutlery_material:               STRICT ENUM, REQUIRED          # plastic | wooden | bamboo | none
  napkins_provided:               boolean, REQUIRED

fees:
  packaging_fee_inr:              INR_INTEGER, REQUIRED
  service_fee_inr:                INR_INTEGER, REQUIRED
  platform_fee_inr:               INR_INTEGER, REQUIRED
  surge_fee_inr:                  INR_INTEGER, REQUIRED          # 0 if no surge
  surge_reason:                   STRICT ENUM, REQUIRED          # see §6 ("none" if no surge)

minimum_order_inr:                INR_INTEGER, REQUIRED
free_delivery_threshold_inr:      INR_INTEGER, REQUIRED          # 0 if no free delivery
gst_rate_pct:                     float, REQUIRED                 # 5 for restaurants typically

photos:
  thumbnail_url:                  URL, REQUIRED
  hero_url:                       URL, REQUIRED
  ambience_photos_url:            URL, REQUIRED                  # JSON manifest

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               # "Swiggy"
  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

DishSummary (in SearchResult.surfaced_dishes)

id:                               string, REQUIRED
name:                             string, REQUIRED
description:                      string, REQUIRED
price_inr:                        INR_INTEGER, REQUIRED
mrp_inr:                          INR_INTEGER, REQUIRED          # equals price if no discount
photo_url:                        URL, REQUIRED
photo_authenticity_verified:      boolean, REQUIRED
photo_ai_generated:               boolean, REQUIRED               # MUST be false for v1
veg_or_non_veg:                   STRICT ENUM, REQUIRED           # veg | non_veg | egg | vegan | jain
spice_level:                      STRICT ENUM, REQUIRED           # see §6
allergens_present:                array<enum>, REQUIRED          # see §6, may be empty
dietary_tags:                     array<enum>, REQUIRED          # see §6, may be empty
serves_count:                     int, REQUIRED, ≥1
calories_kcal:                    int, REQUIRED                   # 0 only if genuinely unknown — partner must collect
protein_g:                        float, REQUIRED
carbs_g:                          float, REQUIRED
fat_g:                            float, REQUIRED
fiber_g:                          float, REQUIRED
contains_msg:                     boolean, REQUIRED
contains_palm_oil:                boolean, REQUIRED
oil_used:                         STRICT ENUM, REQUIRED          # see §6
sugar_g:                          float, REQUIRED
sodium_mg:                        int, REQUIRED
popularity_rank:                  int, REQUIRED                   # 1 = most ordered at this restaurant
order_count_30day:                int, REQUIRED, ≥0
average_dish_rating:              float, REQUIRED, 0-5
review_count:                     int, REQUIRED, ≥0
recommended:                      boolean, REQUIRED               # restaurant's pick
dish_freshness_iso:               ISO_DATETIME, REQUIRED          # last menu update for this dish

RestaurantMenu (returned by get_restaurant_menu)

restaurant:                       Restaurant, REQUIRED
sections:                         array<MenuSection>, REQUIRED, ≥1
last_menu_updated_iso:            ISO_DATETIME, REQUIRED
deals:                            array<Deal>, REQUIRED, may be empty
combos:                           array<Combo>, REQUIRED, may be empty
chef_recommendations:             array<dish_id>, REQUIRED, ≥0
seasonal_specials:                array<dish_id>, REQUIRED, ≥0
new_additions:                    array<dish_id>, REQUIRED, ≥0
high_protein_picks:               array<dish_id>, REQUIRED, ≥0
low_calorie_picks:                array<dish_id>, REQUIRED, ≥0

MenuSection

section_id:                       string, REQUIRED
section_name:                     string, REQUIRED               # "Starters", "Biryani"
section_kind:                     STRICT ENUM, REQUIRED          # see §6
sort_order:                       int, REQUIRED
dishes:                           array<DishSummary>, REQUIRED, ≥1

Deal and Combo

deal_id:                          string, REQUIRED
title:                            string, REQUIRED
description:                      string, REQUIRED
discount_kind:                    STRICT ENUM, REQUIRED          # flat_inr | pct | bogo | bundle
discount_value:                   float, REQUIRED                 # 100 = ₹100 flat OR 20 = 20%
min_cart_inr:                     INR_INTEGER, REQUIRED
max_discount_inr:                 INR_INTEGER, REQUIRED
valid_from_iso:                   ISO_DATETIME, REQUIRED
valid_until_iso:                  ISO_DATETIME, REQUIRED
applies_to_dish_ids:              array<string>, REQUIRED, ≥0
combinable_with_other_deals:      boolean, REQUIRED
auto_applied:                     boolean, REQUIRED
coupon_code_required:             boolean, REQUIRED
coupon_code:                      string, REQUIRED               # "" if not required
funder:                           STRICT ENUM, REQUIRED          # restaurant | partner | tomo
                                                                  # MUST be restaurant or partner — TOMO never funds discounts

DishDetail (returned by get_dish_detail)

Superset of DishSummary. Adds:

ingredients:                      array, REQUIRED, ≥1
  - name:                         string, REQUIRED                # "Basmati rice"
    is_local_sourced:             boolean, REQUIRED
    organic:                      boolean, REQUIRED
    main_ingredient:              boolean, REQUIRED
preparation_method:               STRICT ENUM, REQUIRED           # see §6
prep_time_minutes:                int, REQUIRED, ≥1
cook_time_minutes:                int, REQUIRED, ≥1
customizations:                   array<Customization>, REQUIRED, may be empty
nutritional_disclaimer:           string, REQUIRED                # "" allowed
allergen_facility:                STRICT ENUM, REQUIRED           # see §6 (e.g. shared_kitchen_with_nuts)
photos:                           array<URL>, REQUIRED, ≥1
recent_review_excerpts:           array, REQUIRED, ≥0
  - excerpt:                      string, REQUIRED
    score:                        float, REQUIRED, 0-5
    verified_purchase:            boolean, REQUIRED
    review_date:                  ISO_DATE, REQUIRED

Customization

customization_id:                 string, REQUIRED
group_name:                       string, REQUIRED                # "Choose spice level"
selection_kind:                   STRICT ENUM, REQUIRED           # single | multiple
required:                         boolean, REQUIRED
min_selections:                   int, REQUIRED                   # 1 if required, 0 otherwise
max_selections:                   int, REQUIRED
options:                          array, REQUIRED, ≥1
  - option_id:                    string, REQUIRED
    name:                         string, REQUIRED
    delta_inr:                    int, REQUIRED                   # +30, -0, etc.
    veg_or_non_veg:               STRICT ENUM, REQUIRED
    default_selected:             boolean, REQUIRED

CartQuote (returned by compute_cart_total)

items:                            array<CartItem>, REQUIRED, ≥1
subtotal_inr:                     INR_INTEGER, REQUIRED
discount_inr:                     INR_INTEGER, REQUIRED, ≥0
discount_breakdown:               array, REQUIRED, may be empty
  - deal_id:                      string, REQUIRED
    discount_inr:                 INR_INTEGER, REQUIRED
delivery_fee_inr:                 INR_INTEGER, REQUIRED
packaging_fee_inr:                INR_INTEGER, REQUIRED
service_fee_inr:                  INR_INTEGER, REQUIRED
platform_fee_inr:                 INR_INTEGER, REQUIRED
surge_fee_inr:                    INR_INTEGER, REQUIRED
gst_inr:                          INR_INTEGER, REQUIRED
total_inr:                        INR_INTEGER, REQUIRED
estimated_delivery_iso:           ISO_DATETIME, REQUIRED
quote_valid_until_iso:            ISO_DATETIME, REQUIRED          # quote good for 5min typically

CartItem

dish_id:                          string, REQUIRED
quantity:                         int, REQUIRED, ≥1
unit_price_inr:                   INR_INTEGER, REQUIRED
customizations_applied:           array<option_id>, REQUIRED      # may be empty
line_total_inr:                   INR_INTEGER, REQUIRED
out_of_stock:                     boolean, REQUIRED               # in case it just sold out

OrderTrack (returned by track_order)

order_ref:                        string, REQUIRED
status:                           STRICT ENUM, REQUIRED           # see §6
status_updated_iso:               ISO_DATETIME, REQUIRED
status_history:                   array, REQUIRED, ≥1
  - status:                       STRICT ENUM, REQUIRED
    iso:                          ISO_DATETIME, REQUIRED
estimated_delivery_iso:           ISO_DATETIME, REQUIRED
revised_eta_minutes:              int, REQUIRED                   # current best estimate
delay_reason:                     STRICT ENUM, REQUIRED           # none | rain | traffic | restaurant_busy | rider_unavailable | other
rider:                            object, REQUIRED                # see Rider below
restaurant_status:                STRICT ENUM, REQUIRED           # see §6
support_phone:                    string, REQUIRED
support_email:                    string, REQUIRED

Rider

rider_id:                         string, REQUIRED
display_name:                     string, REQUIRED                # first name only
phone_masked:                     string, REQUIRED                # "+91 98XXX XX234"
vehicle_kind:                     STRICT ENUM, REQUIRED           # bike | bicycle | ev_bike | car | walking
vehicle_plate_masked:             string, REQUIRED                # "•• 1234"
photo_url:                        URL, REQUIRED
rating_avg:                       float, REQUIRED, 0-5
deliveries_completed_total:       int, REQUIRED, ≥0
in_app_chat_supported:            boolean, REQUIRED
in_app_call_supported:            boolean, REQUIRED
location:
  current_lat:                    float, REQUIRED
  current_lng:                    float, REQUIRED
  location_updated_iso:           ISO_DATETIME, REQUIRED
  status:                         STRICT ENUM, REQUIRED           # see §6
  eta_minutes:                    int, REQUIRED

Forbidden fields

paid_placement_score | ad_bid | sponsored_rank | promotion_priority |
artificial_urgency_text | fake_recent_order_text |
auto_inflate_rating | seasonal_marketing_label |
ai_generated_dish_photo (always must equal false in photo_ai_generated) |
hidden_subsidy_text | restaurant_paid_for_top_placement

5. CONTROLLED VOCABULARIES

Restaurant.kind

restaurant | cloud_kitchen | cafe | dessert_parlor | bakery | sweet_shop |
food_truck | tiffin_service | catering_kitchen | qsr | ice_cream_parlor

Restaurant.sub_kind

fine_dining | casual_dining | family_restaurant | fast_casual | quick_service |
buffet | a_la_carte | thali_house | meals_house | tiffin_only | dabba_kitchen

cuisines (Restaurant.cuisines, preferences.cuisines_wanted, dish.dietary_tags)

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 | poke | salad_bar | dessert | bakery | breakfast |
healthy | high_protein | low_carb | keto | gluten_free | sugar_free

party.occasion

casual_dinner | family_dinner | date_night | office_lunch | office_dinner |
work_late | midnight_craving | breakfast | brunch | tea_time | snack |
party | gathering | celebration

dietary_filters and dish.dietary_tags

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 and dish.allergens_present

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

spice_level and dish.spice_level

mild | medium | spicy | very_spicy

dish.veg_or_non_veg

veg | non_veg | egg | vegan | jain

dish.preparation_method

deep_fried | shallow_fried | grilled | tandoor | baked | steamed |
boiled | sauteed | raw | smoked | sun_cooked | bbq | air_fried |
slow_cooked | pressure_cooked | live_grill

dish.allergen_facility

dedicated_kitchen | shared_kitchen_no_high_risk | shared_kitchen_with_nuts |
shared_kitchen_with_dairy | shared_kitchen_with_seafood | shared_kitchen_all

dish.oil_used

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

Restaurant.packaging.primary_material

plastic | bagasse | aluminum | paper | cardboard | banana_leaf | reusable_steel |
mixed

Restaurant.packaging.cutlery_material

plastic | wooden | bamboo | none

Restaurant.fees.surge_reason

none | rain | weekend | festival | late_night | high_demand | rider_shortage

Restaurant.health_inspection_authority

fssai | local_municipality | none

MenuSection.section_kind

starters | mains | breads | rice | biryani | desserts | beverages |
combos | sides | accompaniments | breakfast_specials | seasonal | chef_specials |
healthy | kids_menu | jain_section | vegan_section

Deal.discount_kind

flat_inr | pct | bogo | bundle | second_item_pct | free_item_with_min_cart

Customization.selection_kind

single | multiple

OrderTrack.status

pending_confirmation | accepted | preparing | ready_for_pickup |
rider_assigned | rider_at_restaurant | picked_up | en_route |
nearby | delivered | cancelled | failed_delivery

OrderTrack.restaurant_status

open_taking_orders | open_busy | preparing_yours | done_with_yours |
closed | maintenance

Rider.vehicle_kind

bike | bicycle | ev_bike | car | walking

Rider.location.status

heading_to_restaurant | at_restaurant | picking_up | on_the_way |
nearby | reached

delivery_address.preferred_packaging

biodegradable_or_recyclable | any | reusable_only

merchant_id resolution order

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

6. TTBS DIMENSIONS

Per-domain weights (locked)

food: { time: 0.30, taste: 0.35, budget: 0.20, safety: 0.15 }

TIME

SIGNALS USED:
  - eta.total_eta_minutes                          weight 0.45
  - eta.current_capacity_pct (lower = better)      weight 0.20
  - distance_from_user_km                          weight 0.15
  - operating_hours.is_open_now                    HARD FILTER
  - delivery_partner_score                         weight 0.10
  - estimated_pickup_minutes                       weight 0.10

USER BAND HANDLING:
  - "asap" → time-weight up to 0.45 (steals from taste)
  - "scheduled" → time still matters but less; window flexibility allowed

TASTE

SIGNALS USED:
  - ratings.overall_score                          weight 0.20
  - ratings.recent_30day_score                     weight 0.20
  - ratings.food_quality_score                     weight 0.15
  - cuisines_match_pct (intersection w/ user)      weight 0.20
  - dish_match_signal (popularity_rank of surfaced dishes) weight 0.15
  - chef_recommendations / seasonal_specials       weight 0.10

USER BAND HANDLING:
  - dietary_filters HARD FILTER (no compromise)
  - allergens_to_avoid HARD FILTER
  - jain_meal_required, halal_required HARD FILTER
  - cuisines_wanted not empty → boost cuisine match weight to 0.30

BUDGET

SIGNALS USED:
  - dish median price vs band:
      ok    → 0–33rd percentile of (cuisine, city) market
      good  → 33rd–66th
      great → 66th+
  - delivery_fee_inr (lower = better)               weight 0.20
  - packaging_fee + platform_fee + service_fee      weight 0.20
  - surge_fee_inr present?                          penalty 0.10
  - free_delivery_threshold reached → bonus

HARD FILTERS:
  - any restaurant whose minimum_order_inr > preferences.budget_max_inr_total
  - any quote that would exceed budget after fees

USER BAND HANDLING:
  - "ok" users see fees magnified
  - "great" users see fees de-emphasized

SAFETY

SIGNALS USED:
  - trust.fssai_grade (A=high, B=mid, C=low, unrated=fail) weight 0.30
  - trust.cleanliness_audit_score (>80=high)         weight 0.20
  - trust.food_handler_kyc_pct (>80=high)            weight 0.15
  - packaging.spill_proof_seal                       weight 0.10
  - packaging.uses_single_use_plastic
      (penalty if user.single_use_plastic_free)     weight 0.10
  - allergens_present overlap w/ user.allergens_to_avoid HARD FILTER (drop dish)
  - rider photo + KYC presence                       weight 0.05
  - tomo_field_team_audited                          weight 0.10

HARD FILTERS:
  - jain_meal_required → drop non-jain
  - halal_required → drop non-halal
  - single_use_plastic_free → drop plastic-only kitchens
  - any allergen present in user's avoidance list → drop dish (not restaurant)

Hidden ranking factor

information_completeness_score weight 0.10 in merged pool.


7. COMPLETION CONTRACT

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

{
  "intent":            "food.order_delivery",
  "intent_version":    "v1.0.0",
  "external_id":       "SWIGGY-ORDER-XYZ123",
  "amount_inr":         842,
  "closed_at":         "2026-05-09T20:42:00+05:30",
  "request_id":        "req_8m4p9t_...",
  "status":            "delivered",
  "order_ref":         "SWIGGY-ORDER-XYZ123",
  "merchant_id":       "ChIJxxxxx",
  "restaurant_id":     "swiggy_rest_28342",
  "delivered_at":      "2026-05-09T20:42:00+05:30",
  "delivery_eta_minutes_promised": 35,
  "delivery_eta_minutes_actual":   34,
  "items_count":        3,
  "currency":          "INR",
  "fees_breakdown_total_inr": 122,
  "rider_rating_given": null,
  "restaurant_rating_given": null,
  "notes":             ""
}

Status enum: delivered | failed_delivery | cancelled_by_user | cancelled_by_restaurant | cancelled_no_rider

Signing same as universal scheme.


8. WIDGET

WIDGET TYPE:        food_menu_results
SOURCE:             src/widgets/types.ts
TYPE NAME:          FoodMenuResultsPayload
RENDERED IN:        components/widgets/FoodMenuResultsWidget.tsx

Default: 3 surfaced restaurants in collapsed view, expandable. Per restaurant: thumbnail, cuisines, ETA pill, rating, top 2-3 surfaced dishes with photo+price. Tap → menu drawer → cart → checkout.


9. CACHING POLICY

Call TTL Rationale
search_restaurants_and_dishes 30s ETAs and prices shift fast, especially during surge
get_restaurant_menu 5min menus rarely change
get_dish_detail 5min static-ish
compute_cart_total 5min quote_valid_until_iso enforced server-side too
track_order 0s always live
cancel_order 0s
Failure responses 0s

10. ERROR CODES

Standard universal codes plus:

Code HTTP Meaning TOMO behavior
RESTAURANT_CLOSED 409 restaurant just closed drop result, refresh
OUT_OF_STOCK_DISH 409 dish unavailable mark item, recompute cart
DELIVERY_OUT_OF_RANGE 400 address outside delivery zone surface to user
MINIMUM_ORDER_NOT_MET 400 cart below minimum_order_inr surface to user
RIDER_UNAVAILABLE 503 no riders to assign retry with delay or fallback partner
PAYMENT_DECLINED 402 gateway rejected surface
RESTAURANT_REJECTED_ORDER 409 restaurant declined post-acceptance refund + offer alternates

11. SANDBOX → PRODUCTION CHECKLIST

[ ] All §2 inputs validated, request_id echoed
[ ] search_restaurants_and_dishes returns ≥10 restaurants for "Hyderabad + biryani + 35min ETA"
[ ] All §4 required fields populated with REAL data (no fixtures)
[ ] photo_ai_generated == false on every dish + restaurant photo
[ ] FSSAI license number + grade present on every restaurant
[ ] Allergens_present complete (TOMO sample-tests vs partner's website)
[ ] Nutritional macros (calories, protein, carbs, fat, fiber) present on every dish
[ ] compute_cart_total honors deals, surge, GST exactly
[ ] place_order returns valid order_ref within SLA p95
[ ] track_order returns rider location updates ≤10s old
[ ] cancel_order respects cancellation policy + refunds correctly
[ ] CPC webhook arrives within 60s of delivery
[ ] HMAC verification passes
[ ] No forbidden fields in any response
[ ] No paid placement / sponsored ranking detected (TOMO 1% sample audit)
[ ] customer_support phone reachable + 24x7 if claimed
[ ] FSSAI license uploaded + verified
[ ] GSTIN uploaded
[ ] Privacy policy URL live
[ ] All restaurants have an attached health_inspection report

12. ANTI-FABRICATION RULES

RULE 1 — No paid placement signals (universal)
RULE 2 — No artificial urgency
  high_demand=true requires order_count_30day in top 10% of partner's network
  AND eta surge present. Fake scarcity → listing rejection.

RULE 3 — Ratings must be unrounded floats, sourced from real verified orders
  TOMO samples 1% of orders + cross-checks against partner's review system.

RULE 4 — No AI-generated dish photos
  photo_ai_generated MUST be false. AI photos = restaurant rejection.

RULE 5 — Nutritional macros must be honest within reasonable tolerance
  (±15% on calories vs USDA-published references for known dishes).
  TOMO field samples sanity-check.

RULE 6 — Allergens_present must be complete
  Missing an allergen that turns out to be present = compliance breach.
  Partners cite partner's QC chain.

RULE 7 — Deals.funder must be restaurant or partner — never tomo
  TOMO does not subsidize. Any deal labelled tomo-funded = listing rejection.

RULE 8 — Surge fees must be backed by surge_reason enum
  No "platform fee" surges in disguise. TOMO checks surge_fee_inr correlates
  with surge_reason in non-surge windows.

RULE 9 — No commission-based response shaping
  Same restaurant order on TOMO and on partner's app must yield same total.
  TOMO 1% sample audits cross-check.

RULE 10 — Customer support fields honest
  customer_support_24x7=true means human within 5 min. TOMO field-tests.

VERSION HISTORY

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

PARTNER QUICKSTART

  1. Read _TEMPLATE.md and _INTENT_CATALOG.md
  2. Read this file
  3. Implement 7 tools per §3
  4. Return all §4 fields populated, no skips
  5. Use only §5 vocabularies
  6. POST signed CPC webhook on delivery
  7. Pass §11 checklist
  8. Get status: live