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
- Read
_TEMPLATE.mdand_INTENT_CATALOG.md - Read this file
- Implement 7 tools per §3
- Return all §4 fields populated, no skips
- Use only §5 vocabularies
- POST signed CPC webhook on delivery
- Pass §11 checklist
- Get
status: live