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.subscribe_tiffin

food.subscribe_tiffin — Full Intent Specification

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

Tiffin subscription is recurring meal delivery (1-3 meals/day, fixed window, billed weekly/monthly). It differs from per-order delivery in five locked ways: (a) recurrence block defines the schedule; (b) meal_plan is a rotating menu, not a single dish; (c) billing_period aggregates many meals; (d) pause/resume + skip-day are first-class; (e) safety stays at 0.10 (vetted partner over time) but allergen/dietary HARD FILTERS still apply. Closest cousin: mobility.book_recurring_commute. Per-meal CPC + closure CPC like recurring_commute.


1. NATURAL LANGUAGE COVERAGE

Classifies IN

  • "subscribe to lunch tiffin for the month"
  • "tiffin service near me"
  • "daily home-style meals to office"
  • "monthly tiffin Madhapur"
  • "veg tiffin subscription Mon-Fri"
  • "Andhra meals home delivery subscription"
  • "weekly dabba service for me and roommate"
  • "Jain tiffin subscription"
  • "two tiffin per day breakfast and lunch"

Classifies OUT — borderline NO

  • "biryani for dinner today" → food.order_delivery
  • "table for 4 at Paradise" → food.book_dine_in
  • "chef at home for the week" → food.book_chef_at_home
  • "groceries for the month" → grocery.subscribe_grocery (separate domain)

MULTI-INTENT TRIGGERS

  • "tiffin subscription and weekly grocery" → food.subscribe_tiffin + grocery.order_delivery
  • "lunch tiffin and daily commute cab" → food.subscribe_tiffin + mobility.book_recurring_commute
  • "tiffin subscription and KYC verify" → food.subscribe_tiffin + compliance.verify_kyc

2. INPUT — TOMO → PROVIDER

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

  "subscription_kind": "monthly_lunch_only",

  "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",
    "place_kind":     "office"
  },

  "recurrence": {
    "pattern":               "mon_to_sat",
    "meals_per_day":         "lunch_only",
    "lunch_delivery_window": {
      "start_iso_local":     "12:30:00",
      "end_iso_local":       "13:30:00"
    },
    "breakfast_delivery_window": null,
    "dinner_delivery_window":   null,
    "skip_dates":            ["2026-05-15", "2026-05-22"],
    "active_from_iso":       "2026-05-12",
    "active_until_iso":      "2026-06-12"
  },

  "billing": {
    "period_kind":           "monthly",
    "auto_renew":            true,
    "billing_day_of_month":  10
  },

  "diners": {
    "diner_count":           1,
    "diner_breakdown":       {"adults": 1, "children": 0, "infants": 0},
    "dietary_filters":       ["veg", "no_onion_no_garlic"],
    "allergens_to_avoid":    ["peanuts"],
    "preferences_text_hint": "low spice, more variety in vegetables"
  },

  "preferences": {
    "cuisine_preferred":              "andhra",
    "cuisine_acceptable":             ["andhra", "south_indian", "north_indian"],
    "spice_level_max":                "medium",
    "regional_authenticity_required": true,
    "menu_rotation_minimum_days":     14,
    "tiffin_packaging":               "stainless_steel_dabba",
    "delivery_kind_preferred":        "home_delivery",
    "budget_band":                    "good",
    "budget_max_inr_per_period":      4500,
    "max_eta_minutes":                15,
    "kitchen_visit_friendly":         true,
    "delivery_personnel_kyc_required": true
  },

  "context": {
    "user_locale":          "en-IN",
    "user_currency_pref":   "INR",
    "trip_purpose":         "office_lunch",
    "trust_signals": {
      "is_repeat_customer":          false,
      "prior_subscriptions_with_partner": 0,
      "user_account_age_days":       312,
      "verified_address":            true,
      "verified_phone":              true
    }
  }
}
Field Type Constraint Notes
intent string REQUIRED, equals "food.subscribe_tiffin"
subscription_kind enum REQUIRED, see §5
delivery_address.place_kind enum REQUIRED
recurrence.pattern enum REQUIRED, see §5
recurrence.meals_per_day enum REQUIRED, STRICT breakfast_only | lunch_only | dinner_only | breakfast_lunch | breakfast_dinner | lunch_dinner | three_meals
recurrence.lunch_delivery_window object REQUIRED, may be null required if meals_per_day includes lunch
recurrence.skip_dates array REQUIRED, may be empty
recurrence.active_from_iso ISO_DATE REQUIRED
recurrence.active_until_iso ISO_DATE REQUIRED
billing.period_kind enum REQUIRED, STRICT weekly | monthly | quarterly
billing.auto_renew bool REQUIRED
billing.billing_day_of_month int REQUIRED, 1-31
diners.diner_count int REQUIRED, ≥1
diners.diner_breakdown object REQUIRED adults + children + infants
diners.dietary_filters array REQUIRED, may be empty see §5
diners.allergens_to_avoid array REQUIRED, may be empty
diners.preferences_text_hint string REQUIRED, may be empty partner-side LLM hints
preferences.cuisine_preferred enum REQUIRED, see §5
preferences.cuisine_acceptable array REQUIRED, ≥1
preferences.regional_authenticity_required bool REQUIRED
preferences.menu_rotation_minimum_days int REQUIRED, ≥1 meals shouldn't repeat sooner
preferences.tiffin_packaging enum REQUIRED, see §5
preferences.delivery_kind_preferred enum REQUIRED, STRICT home_delivery | pickup_at_kitchen | central_locker_pickup
preferences.budget_max_inr_per_period INR_INTEGER REQUIRED
preferences.max_eta_minutes int REQUIRED tolerance against window
preferences.kitchen_visit_friendly bool REQUIRED partner allows user inspection
preferences.delivery_personnel_kyc_required bool REQUIRED

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


3. PROVIDER TOOLS

Tool 1: get_tiffin_subscription_estimates

PURPOSE:        return subscription options across kitchens + plans
INPUT:          §2 request body
OUTPUT:         { options: TiffinSubscriptionOption[], result_token, expires_at }
SLA:            p50 < 800ms, p95 < 1500ms
RATE LIMIT:     ≤ 1/sec per user

Tool 2: get_kitchen_detail_with_menu

PURPOSE:        kitchen profile + 14-day rolling menu
INPUT:          { kitchen_id, request_id }
OUTPUT:         KitchenDetail (§4)
SLA:            p95 < 800ms

Tool 3: book_tiffin_subscription

PURPOSE:        commit subscription + first delivery
INPUT:          { option_id, payment_token, request_id, idempotency_key, user_phone, otp_required }
OUTPUT:         { subscription_ref, status, fare_quote, first_delivery_iso, billing_setup_status }
SLA:            p95 < 5000ms
IDEMPOTENCY:    REQUIRED on idempotency_key

Tool 4: get_subscription_state

PURPOSE:        live state with deliveries-this-period summary
INPUT:          { subscription_ref, request_id }
OUTPUT:         TiffinSubscriptionState (§4)
SLA:            p95 < 600ms
RATE LIMIT:     ≤ 1 every 60s

Tool 5: pause_subscription

INPUT:          { subscription_ref, pause_from_iso, pause_until_iso, reason, request_id }
OUTPUT:         { status, billing_credit_inr, resume_iso }
SLA:            p95 < 1500ms

Tool 6: resume_subscription

INPUT:          { subscription_ref, resume_from_iso, request_id }
OUTPUT:         { status, next_billing_iso, next_delivery_iso }
SLA:            p95 < 1500ms

Tool 7: skip_meal_for_date

INPUT:          { subscription_ref, skip_date_iso, meal_kind, request_id }
OUTPUT:         { status, billing_adjustment_inr }
SLA:            p95 < 1000ms

Tool 8: swap_meal_or_request_special

PURPOSE:        request a swap for one day's meal
INPUT:          { subscription_ref, target_date_iso, meal_kind, swap_request_text, request_id }
OUTPUT:         { status, swap_acknowledged, fare_delta_inr }
SLA:            p95 < 1500ms

Tool 9: cancel_subscription

INPUT:          { subscription_ref, reason, cancel_immediately, request_id }
OUTPUT:         { status, refund_amount_inr, last_delivery_iso, refund_processing_days }
SLA:            p95 < 2000ms

Tool 10: track_today_meal

PURPOSE:        live tracking for today's delivery
INPUT:          { subscription_ref, meal_kind, request_id }
OUTPUT:         { status, eta_minutes, rider_name, rider_phone_masked, rider_lat, rider_lng }
SLA:            p95 < 400ms
RATE LIMIT:     ≤ 1 every 30s

Tool 11: rate_meal

PURPOSE:        rate a specific meal post-delivery
INPUT:          { subscription_ref, meal_iso, meal_kind, rating_5star, comment, request_id }
OUTPUT:         { acknowledged: true }
SLA:            p95 < 800ms

All eleven REQUIRED.


4. RESPONSE SHAPE

TiffinSubscriptionOption

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

subscription_kind:                STRICT ENUM, REQUIRED

kitchen:
  id:                             string, REQUIRED
  merchant_id:                    string, REQUIRED
  name:                           string, REQUIRED
  brand:                          string, REQUIRED
  cuisine_primary:                STRICT ENUM, REQUIRED
  cuisines_offered:               array<enum>, REQUIRED, ≥1
  veg_only_kitchen:               boolean, REQUIRED
  jain_meals_available:           boolean, REQUIRED
  halal_meals_available:          boolean, REQUIRED
  satvik_meals_available:         boolean, REQUIRED
  no_onion_no_garlic_available:   boolean, REQUIRED
  regional_authenticity_certified: boolean, REQUIRED
  authenticity_authority:         STRICT ENUM, REQUIRED
  total_subscribers:              int, REQUIRED, ≥0
  kitchen_kind:                   STRICT ENUM, REQUIRED       # cloud_kitchen | home_kitchen | dabba_kitchen | tiffin_co_op
  partner_account_age_days:       int, REQUIRED, ≥0
  address:
    line_1:                       string, REQUIRED
    neighborhood:                 string, REQUIRED
    city:                         string, REQUIRED
    pincode:                      string, REQUIRED
    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
    food_quality_score:           float, REQUIRED, 0-5
    portion_score:                float, REQUIRED, 0-5
    consistency_score:            float, REQUIRED, 0-5
    delivery_punctuality_score:   float, REQUIRED, 0-5
    packaging_score:              float, REQUIRED, 0-5
  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
    kitchen_visit_friendly:       boolean, REQUIRED
    last_kitchen_inspection_iso:  ISO_DATETIME, REQUIRED
    tomo_field_team_audited:      boolean, REQUIRED
  photos:
    thumbnail_url:                URL, REQUIRED
    hero_url:                     URL, REQUIRED
    kitchen_photos_url:           URL, REQUIRED
    food_photos_url:              URL, REQUIRED
    ai_generated_photos:          boolean, REQUIRED            # MUST be false

meal_plan:
  meals_per_day:                  STRICT ENUM, REQUIRED
  rolling_menu_days:              int, REQUIRED, ≥7
  menu_rotation:                  array, REQUIRED, ≥7
    - day_index:                  int, REQUIRED
      day_label:                  string, REQUIRED            # "Mon Week 1"
      meals:                      array, REQUIRED, ≥1
        - meal_kind:              STRICT ENUM, REQUIRED        # breakfast | lunch | dinner
          dishes:                 array<TiffinDish>, REQUIRED, ≥1

fare:
  total_period_inr:               INR_INTEGER, REQUIRED
  per_meal_avg_inr:               INR_INTEGER, REQUIRED
  base_per_meal_inr:              INR_INTEGER, REQUIRED       # standalone-equivalent
  subscription_discount_pct:      float, REQUIRED, 0-1
  delivery_fee_inr_per_period:    INR_INTEGER, REQUIRED
  packaging_fee_inr_per_period:   INR_INTEGER, REQUIRED
  platform_fee_inr_per_period:    INR_INTEGER, REQUIRED
  gst_inr_per_period:             INR_INTEGER, REQUIRED
  fare_breakdown_text:            string, REQUIRED
  is_upfront_fare:                boolean, REQUIRED
  fare_locked_until_iso:          ISO_DATETIME, REQUIRED

billing:
  period_kind:                    STRICT ENUM, REQUIRED
  charge_iso:                     ISO_DATETIME, REQUIRED
  payment_method_required:        STRICT ENUM, REQUIRED
  prorated_first_period:          boolean, REQUIRED
  proration_method:               STRICT ENUM, REQUIRED
  refund_policy:                  STRICT ENUM, REQUIRED

delivery_meta:
  delivery_kind:                  STRICT ENUM, REQUIRED
  delivery_personnel_kyc_pct:     int, REQUIRED, 0-100
  delivery_partner_kind:          STRICT ENUM, REQUIRED        # in_house | third_party_aggregator | partner_owned
  delivery_eta_buffer_minutes:    int, REQUIRED                # std deviation from window
  late_delivery_30day_pct:        float, REQUIRED, 0-1
  cold_chain_compliant:           boolean, REQUIRED            # if perishable
  spill_proof_seal:               boolean, REQUIRED

packaging:
  primary_packaging:              STRICT ENUM, REQUIRED
  cutlery_provided:               boolean, REQUIRED
  cutlery_material:               STRICT ENUM, REQUIRED
  napkins_provided:               boolean, REQUIRED
  reusable_dabba_supported:       boolean, REQUIRED
  user_keeps_dabba:               boolean, REQUIRED
  dabba_pickup_for_reuse:         boolean, REQUIRED            # tiffin co-op model

cancellation:
  free_cancel_within_first_n_meals: int, REQUIRED, ≥0
  cancel_charge_inr_per_remaining_period: INR_INTEGER, REQUIRED
  exit_charge_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_subscriptions_served_30d: int, REQUIRED, ≥0
  partner_subscription_completion_rate_30d: float, REQUIRED, 0-1

TiffinDish

id:                               string, REQUIRED
name:                             string, REQUIRED
description:                      string, REQUIRED
veg_or_non_veg:                   STRICT ENUM, REQUIRED
spice_level:                      STRICT ENUM, REQUIRED
allergens_present:                array<enum>, REQUIRED
dietary_tags:                     array<enum>, REQUIRED
serves_count:                     int, REQUIRED, ≥1
calories_kcal:                    int, REQUIRED
protein_g:                        float, REQUIRED
carbs_g:                          float, REQUIRED
fat_g:                            float, REQUIRED
fiber_g:                          float, REQUIRED
contains_palm_oil:                boolean, REQUIRED
oil_used:                         STRICT ENUM, REQUIRED
sodium_mg:                        int, REQUIRED
photo_url:                        URL, REQUIRED
photo_ai_generated:               boolean, REQUIRED            # MUST be false
preparation_method:               STRICT ENUM, REQUIRED
freshness_iso:                    ISO_DATETIME, REQUIRED       # cooked-at time

KitchenDetail (returned by get_kitchen_detail_with_menu)

Superset of kitchen block in TiffinSubscriptionOption plus full 14-day rolling menu, expanded reviews, sample-meal photos, kitchen-walk-through video URL.

TiffinSubscriptionState (returned by get_subscription_state)

subscription_ref:                 string, REQUIRED
status:                           STRICT ENUM, REQUIRED
status_updated_iso:               ISO_DATETIME, REQUIRED
status_history:                   array, REQUIRED, ≥1

period_summary:
  current_period_start_iso:       ISO_DATETIME, REQUIRED
  current_period_end_iso:         ISO_DATETIME, REQUIRED
  meals_delivered_this_period:    int, REQUIRED, ≥0
  meals_skipped_by_user:          int, REQUIRED, ≥0
  meals_failed_by_partner:        int, REQUIRED, ≥0
  meals_remaining_this_period:    int, REQUIRED, ≥0
  current_period_inr_charged:     INR_INTEGER, REQUIRED, ≥0

next_meal:
  date_iso:                       ISO_DATE, REQUIRED
  meal_kind:                      STRICT ENUM, REQUIRED
  delivery_window_start_iso:      ISO_DATETIME, REQUIRED
  delivery_window_end_iso:        ISO_DATETIME, REQUIRED
  expected_dishes:                array<dish_id>, REQUIRED, ≥1
  expected_rider_kyc_verified:    boolean, REQUIRED

paused:
  is_paused:                      boolean, REQUIRED
  paused_from_iso:                ISO_DATETIME, REQUIRED
  paused_until_iso:               ISO_DATETIME, REQUIRED

billing_state:
  next_charge_iso:                ISO_DATETIME, REQUIRED
  payment_method_status:          STRICT ENUM, REQUIRED
  auto_renew_active:              boolean, REQUIRED

support_phone:                    string, REQUIRED
support_email:                    string, REQUIRED

Forbidden fields

paid_placement_score | sponsored_rank | promotion_priority |
fake_recent_subscriber_text | auto_inflate_subscriber_count |
ai_generated_photos (must equal false) | hidden_packaging_fee |
fake_food_handler_kyc_pct | undocumented_dabba_deposit

5. CONTROLLED VOCABULARIES

subscription_kind

weekly_breakfast_only | weekly_lunch_only | weekly_dinner_only |
weekly_breakfast_lunch | weekly_lunch_dinner | weekly_three_meals |
monthly_breakfast_only | monthly_lunch_only | monthly_dinner_only |
monthly_breakfast_lunch | monthly_lunch_dinner | monthly_three_meals |
quarterly_lunch_only | quarterly_three_meals

meal_plan.meals_per_day

breakfast_only | lunch_only | dinner_only |
breakfast_lunch | breakfast_dinner | lunch_dinner | three_meals

recurrence.pattern

mon_to_fri | mon_to_sat | every_day | weekdays_only_skip_holidays |
custom_days

delivery_address.place_kind

home | office | school | college | hostel | other

cuisine_*

hyderabadi | south_indian | north_indian | bengali | punjabi | gujarati |
maharashtrian | rajasthani | kerala | tamilian | andhra | konkani | goan |
mughlai | jain | satvik | regional_continental | global_fusion

kitchen.authenticity_authority

none | guru_authority_indian | regional_culinary_council | partner_internal | tomo_food_audit

kitchen.kitchen_kind

cloud_kitchen | home_kitchen | dabba_kitchen | tiffin_co_op | hotel_kitchen

kitchen.trust.fssai_grade

A | B | C | unrated

dietary_filters and dish.dietary_tags

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

allergens

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

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 | sun_cooked | slow_cooked | pressure_cooked

dish.oil_used

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

preferences.tiffin_packaging and packaging.primary_packaging

stainless_steel_dabba | bagasse | aluminum_foil | banana_leaf |
microwave_safe_plastic | paper_box | reusable_silicone | mixed

packaging.cutlery_material

plastic | wooden | bamboo | none

preferences.delivery_kind_preferred and delivery_meta.delivery_kind

home_delivery | pickup_at_kitchen | central_locker_pickup | drop_box_at_office

delivery_meta.delivery_partner_kind

in_house | third_party_aggregator | partner_owned

billing.period_kind

weekly | monthly | quarterly

billing.payment_method_required

upi_autopay | card_recurring_mandate | netbanking_emandate | wallet_auto_recharge

billing.proration_method

exact_per_meal_count | flat_first_period | no_proration

billing.refund_policy

unused_meals_credited | unused_meals_refunded_60pct | unused_meals_refunded_full |
no_refund_after_period_starts

TiffinSubscriptionState.status

pending_first_meal | active | paused_by_user | paused_by_partner |
billing_failed | renewal_failed | cancelled_by_user | cancelled_by_partner |
expired | failed

TiffinSubscriptionState.next_meal.meal_kind

breakfast | lunch | dinner

TiffinSubscriptionState.billing_state.payment_method_status

active | pending_authorization | failed_last_charge | mandate_revoked

cancel_subscription.reason

user_changed_address | unsatisfied_food_quality | unsatisfied_portion_size |
unsatisfied_punctuality | found_alternative | health_dietary_change |
moved_city | medical | other

pause_subscription.reason

travel | medical | hosting_guests | wfh_period | sabbatical | other

merchant_id resolution order

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

6. TTBS DIMENSIONS

Per-domain weights (locked; tiffin override)

food (subscribe_tiffin): { time: 0.20, taste: 0.40, budget: 0.30, safety: 0.10 }

Time stays moderate (window-based, not minute-precise). Taste is the heart of tiffin (variety, regional authenticity, consistency). Budget rises (subscription pricing matters). Safety drops to 0.10 — repeat customer pattern + HARD FILTERS handle critical safety.

TIME

SIGNALS USED:
  - delivery_meta.late_delivery_30day_pct (lower=better) weight 0.40
  - delivery_meta.delivery_eta_buffer_minutes        weight 0.20
  - distance_from_user_km                            weight 0.20
  - billing.refund_policy permissiveness             weight 0.10
  - cancellation.refund_processing_days              weight 0.10

USER BAND HANDLING:
  - "tight 1pm window" → late_delivery_30day_pct < 5%

TASTE

SIGNALS USED:
  - kitchen.ratings.food_quality_score               weight 0.20
  - kitchen.ratings.consistency_score                weight 0.20
  - kitchen.regional_authenticity_certified          weight 0.20
  - cuisine_preferred match                          weight 0.20
  - meal_plan.rolling_menu_days vs preferences.menu_rotation_minimum_days weight 0.10
  - kitchen.kitchen_kind=home_kitchen (authenticity bias) weight 0.10

HARD FILTERS:
  - dietary_filters not satisfiable
  - allergens_to_avoid present in menu_rotation
  - jain_meal_required AND jain_meals_available=false
  - halal_required AND halal_meals_available=false
  - regional_authenticity_required AND regional_authenticity_certified=false

BUDGET

SIGNALS USED:
  - fare.total_period_inr vs band:
      ok    → 0–33rd percentile (city, period)
      good  → 33rd–66th
      great → 66th+
  - fare.subscription_discount_pct (higher=better)   weight 0.30
  - fare.is_upfront_fare=true                        weight 0.20
  - cancellation.cancel_charge_inr_per_remaining_period (lower=better) weight 0.20
  - cancellation.exit_charge_inr (lower=better)      weight 0.15
  - billing.refund_policy permissiveness             weight 0.15

HARD FILTERS:
  - fare.total_period_inr > preferences.budget_max_inr_per_period → drop

SAFETY

SIGNALS USED:
  - kitchen.trust.fssai_grade (A=high)               weight 0.30
  - kitchen.trust.cleanliness_audit_score            weight 0.20
  - kitchen.trust.food_handler_kyc_pct               weight 0.15
  - kitchen.trust.kitchen_visit_friendly             weight 0.10
  - kitchen.trust.tomo_field_team_audited            weight 0.10
  - delivery_meta.delivery_personnel_kyc_pct (>80%)  weight 0.10
  - delivery_meta.cold_chain_compliant (perishable)  weight 0.10
  - delivery_meta.spill_proof_seal                   weight 0.05
  - menu allergens overlap user.allergens_to_avoid   HARD FILTER (drop kitchen)

HARD FILTERS:
  - delivery_personnel_kyc_required AND kitchen kyc_pct < 80% → drop
  - any allergen in menu present in user.allergens_to_avoid → drop kitchen

Hidden ranking factor

information_completeness_score weight 0.10.


7. COMPLETION CONTRACT

Like mobility.book_recurring_commute, this intent has TWO completion events.

Per-period settlement

POST /api/v1/cpc/mcp_provider/{tomo_partner_id}
{
  "intent":            "food.subscribe_tiffin",
  "intent_version":    "v1.0.0",
  "event_kind":        "period_settled",
  "external_id":       "TIFFIN-XYZ",
  "subscription_ref":  "TIFFIN-XYZ",
  "amount_inr":         3800,
  "closed_at":         "2026-06-10T00:01:00+05:30",
  "request_id":        "req_tif_3k9p_...",
  "status":            "period_completed",
  "period_start_iso":  "2026-05-12T00:00:00+05:30",
  "period_end_iso":    "2026-06-09T23:59:59+05:30",
  "meals_delivered":   24,
  "meals_skipped":     2,
  "meals_failed":      0,
  "currency":          "INR"
}

Subscription closure

POST /api/v1/cpc/mcp_provider/{tomo_partner_id}
{
  "intent":            "food.subscribe_tiffin",
  "intent_version":    "v1.0.0",
  "event_kind":        "subscription_closed",
  "external_id":       "TIFFIN-XYZ",
  "subscription_ref":  "TIFFIN-XYZ",
  "amount_inr":         11400,
  "closed_at":         "2026-08-12T00:00:00+05:30",
  "status":            "completed",
  "subscription_started_at": "2026-05-12T00:00:00+05:30",
  "subscription_ended_at":   "2026-08-11T23:59:59+05:30",
  "total_periods_billed": 3,
  "total_meals_delivered": 72,
  "total_meals_skipped":   6,
  "total_meals_failed":    1,
  "currency":          "INR",
  "ratings_pending":     true,
  "notes":              ""
}

Status enum (period): period_completed | period_partial_refund_credited | period_billing_failed Status enum (closure): completed | cancelled_by_user_within_grace | cancelled_by_user_after_grace | cancelled_by_partner | expired_no_renew | failed


8. WIDGET

WIDGET TYPE:        tiffin_subscription_options
SOURCE:             src/widgets/types.ts
TYPE NAME:          TiffinSubscriptionOptionsPayload
RENDERED IN:        components/widgets/TiffinSubscriptionOptionsWidget.tsx

Default: 3 stacked rows showing kitchen name, cuisine, meals/day, fare per period with per-meal-avg, FSSAI grade badge. Tap row → kitchen detail with rolling 14-day menu, kitchen photos, sample meal photos, reviews → "Subscribe". Then TiffinSubscriptionDashboard with period progress + pause/resume + skip-meal + swap-meal controls.


9. CACHING POLICY

Call TTL Rationale
get_tiffin_subscription_estimates 300s inventory + pricing slow
get_kitchen_detail_with_menu 60s menu may rotate
get_subscription_state 30s period summary doesn't change often
book_tiffin_subscription 0s
pause/resume/skip/swap 0s
cancel_subscription 0s
track_today_meal 5s live during delivery window
Failure responses 0s

10. ERROR CODES

Code HTTP Meaning TOMO behavior
NO_KITCHENS_AVAILABLE 503 no kitchen serves the area fall back
OUT_OF_DELIVERY_RANGE 400 address outside coverage surface
OPTION_EXPIRED 410 option_token invalid re-quote
PAYMENT_METHOD_REQUIRED 402 no UPI Autopay / mandate surface
MANDATE_AUTH_FAILED 402 bank mandate auth declined surface
SUBSCRIPTION_NOT_FOUND 404 subscription_ref doesn't exist surface
ALREADY_CANCELLED 409 duplicate cancel idempotent
ALREADY_PAUSED 409 duplicate pause idempotent
BILLING_FAILED 402 last cycle declined dunning flow
MANDATE_REVOKED 401 user revoked at bank re-onboard
WINDOW_INFEASIBLE 400 window not coverable surface
MEAL_NOT_SCHEDULED 404 get for non-scheduled day surface
SWAP_REJECTED 409 partner cannot honor swap surface alternate

11. SANDBOX → PRODUCTION CHECKLIST

[ ] All §2 inputs validated, request_id echoed
[ ] get_tiffin_subscription_estimates returns ≥3 options
[ ] All §4 fields populated with real data
[ ] FSSAI license + grade present
[ ] regional_authenticity_certified backed by partner-internal cert
[ ] photo_ai_generated == false everywhere
[ ] Allergens complete on every menu_rotation dish
[ ] Macros (calories, protein, carbs, fat) on every dish
[ ] book_tiffin_subscription returns valid subscription_ref + first_delivery
[ ] UPI Autopay / mandate setup tested
[ ] First meal delivered on active_from_iso
[ ] track_today_meal returns rider location
[ ] pause/resume/skip/swap flows tested with billing math
[ ] cancel respects free_cancel_within_first_n_meals grace
[ ] Per-period CPC fires on each billing close ≤24h
[ ] Subscription-closure CPC ≤60s of cancel
[ ] HMAC verification passes
[ ] No forbidden fields anywhere
[ ] No paid_placement / sponsored signals
[ ] customer_support_24x7 verified by TOMO call
[ ] Kitchen visit (kitchen_visit_friendly=true) tested
[ ] FSSAI license uploaded
[ ] All delivery personnel KYC artifacts uploaded

12. ANTI-FABRICATION RULES

RULE 1 — No paid placement signals.

RULE 2 — Ratings unrounded floats from real subscribers.

RULE 3 — No fake total_subscribers inflation.
  TOMO compares stated count vs partner CPC ledger; mismatch >5% = breach.

RULE 4 — No fake regional_authenticity_certified.
  Must back claim with auth-authority cert. Self-attesting "regional" without
  cert + serving Punjabi food labelled as Andhra = breach.

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

RULE 6 — Allergens_present must be complete.

RULE 7 — Macros honest within ±15% of computed nutrition.

RULE 8 — No commission-based response shaping.

RULE 9 — Skipped meals credited honestly.
  meals_skipped_by_user must produce billing_credit_inr per proration policy.
  Charging full period when user skipped 8 of 26 meals = breach.

RULE 10 — Failed meals are not absorbed silently.
  meals_failed_by_partner > 0 must trigger user-visible billing credit.
  Hiding failed meals to preserve completion_rate_30d = severe breach.

RULE 11 — Auto-renew honest.
  billing.auto_renew=true requires user notification 7 days before charge.

RULE 12 — Pause/resume math honest.
  pause credit = (paused_days / total_period_days) × period_inr, rounded down.

RULE 13 — Mandate revocation respected.

RULE 14 — Kitchen visit promise honored.
  kitchen_visit_friendly=true means user can show up unannounced during
  business hours and inspect kitchen. Refusing = breach.

RULE 15 — Menu rotation honored.
  rolling_menu_days ≥ user's menu_rotation_minimum_days. Repeating same
  meal twice within stated rotation window = breach.

RULE 16 — Delivery personnel KYC real.
  delivery_personnel_kyc_pct must reflect partner's actual KYC artifact
  collection. Inflating to attract trust-conscious users = breach.

RULE 17 — Reusable dabba pickup honest.
  reusable_dabba_supported=true + dabba_pickup_for_reuse=true means partner
  actually collects + sanitizes dabbas. Charging dabba deposit then not
  collecting = breach.

VERSION HISTORY

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