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

food.order_pickup — Full Intent Specification

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

Pickup is food.order_delivery minus the rider — user goes to restaurant, partner has order ready. The full restaurant + dish + cart + menu shape from food.order_delivery is INHERITED. This spec lists ONLY the deltas: (a) pickup_window replaces delivery_address; (b) no rider/Delivery; (c) pickup_eta_minutes replaces total_eta; (d) typically lower fees (no delivery fee, sometimes no packaging fee); (e) hidden ranking favor for partners with curbside-pickup zones; (f) safety drops to 0.10 (no rider risk, no delivery handover risk).

Inheritance contract: every field in food.order_delivery §2/§4/§5 is REQUIRED here too. Partners that implement this intent MUST also implement food.order_delivery end-to-end. This spec lists ONLY the deltas.


1. NATURAL LANGUAGE COVERAGE

Classifies IN

  • "order biryani for pickup at 8pm"
  • "I'll pick up from Paradise"
  • "place order, I'll come collect"
  • "self-pickup biryani"
  • "order food for takeaway"
  • "I'll come to the restaurant for it"
  • "biryani takeaway no delivery"
  • "schedule pickup at 7:30pm Paradise"
  • "curbside pickup near my office"

Classifies OUT — borderline NO

  • "biryani for dinner" (delivery default) → food.order_delivery
  • "table for 4" → food.book_dine_in
  • "tiffin subscription" → food.subscribe_tiffin
  • "chef at home" → food.book_chef_at_home

MULTI-INTENT TRIGGERS

  • "biryani pickup at 8pm and book a cab home" → food.order_pickup + mobility.book_intracity_ride
  • "pickup biryani and a birthday cake" → food.order_pickup + food.order_cake_or_special

2. INPUT — TOMO → PROVIDER (DELTA over food.order_delivery)

All input fields from food.order_delivery §2 are REQUIRED. REPLACE delivery_address block with pickup_window. ADD pickup_kind.

{
  "intent":          "food.order_pickup",
  "intent_version":  "v1.0.0",
  "request_id":      "req_pkp_8h2k_2026-05-10T18:00:00Z",

  "pickup_kind":     "user_walks_in_to_restaurant",

  "pickup_window": {
    "start_iso":     "2026-05-10T19:55:00+05:30",
    "end_iso":       "2026-05-10T20:15:00+05:30",
    "buffer_minutes_after_window": 15
  },

  "user_arrival_eta_minutes_at_request": 35,
  "user_lat_at_request":                17.4435,
  "user_lng_at_request":                78.3772,

  ... (all other base fields from food.order_delivery §2)
}
Field Type Constraint Notes
intent string REQUIRED, equals "food.order_pickup"
pickup_kind enum REQUIRED, see §5
pickup_window.start_iso ISO_DATETIME REQUIRED when partner has order ready
pickup_window.end_iso ISO_DATETIME REQUIRED when order quality degrades or canceled
pickup_window.buffer_minutes_after_window int REQUIRED, ≥0 grace period
user_arrival_eta_minutes_at_request int REQUIRED, ≥0 drives "ready by" estimate
user_lat_at_request float REQUIRED drives distance calc to restaurant
user_lng_at_request float REQUIRED

REMOVED from food.order_delivery §2:

  • delivery_address (replaced)

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


3. PROVIDER TOOLS (DELTA)

7 of 7 tools from food.order_delivery §3 are REQUIRED with the following modifications:

search_restaurants_and_dishes (modified)

Same shape as base. Returns SearchResult with pickup_eta_minutes instead of delivery_eta_minutes. Filters out restaurants without pickup support.

place_order (modified)

Same shape as base, except:

  • INPUT: replaces delivery_address with pickup_window and pickup_kind
  • OUTPUT: replaces rider_assignment_eta_minutes with pickup_ready_iso and qr_code_for_pickup_url

track_order (modified)

Same shape as base, except OrderTrack returns pickup_ready status instead of rider tracking. No Rider block.

ADD:

Tool 8: confirm_user_pickup

PURPOSE:        partner records that user collected order
INPUT:          { order_ref, qr_code_scanned, pickup_iso, request_id }
OUTPUT:         { acknowledged: true, completion_iso }
SLA:            p95 < 800ms

Tool 9: extend_pickup_window

PURPOSE:        user delays pickup by adding to window
INPUT:          { order_ref, additional_minutes, reason, request_id }
OUTPUT:         { revised_pickup_end_iso, fare_delta_inr, status }
SLA:            p95 < 1500ms

All 9 tools REQUIRED.


4. RESPONSE SHAPE (DELTA)

SearchResult (modified for pickup)

Same as base, REPLACE estimated_delivery_eta_minutes with pickup_ready_eta_minutes. REMOVE delivery_fee_inr (set to 0). ADD:

pickup_ready_eta_minutes:         int, REQUIRED, ≥0
restaurant_pickup_zone:           STRICT ENUM, REQUIRED          # see §5
curbside_pickup_supported:        boolean, REQUIRED
locker_pickup_supported:          boolean, REQUIRED

Restaurant (modified)

Same as base, ADD:

pickup_options:
  walk_in_supported:              boolean, REQUIRED
  curbside_pickup_supported:      boolean, REQUIRED
  curbside_pickup_window_minutes: int, REQUIRED                  # 0 if not supported
  locker_pickup_supported:        boolean, REQUIRED
  locker_address:                 string, REQUIRED                # may be empty
  drive_through_supported:        boolean, REQUIRED
  pickup_zone_kind:               STRICT ENUM, REQUIRED          # see §5
  pickup_qr_code_required:        boolean, REQUIRED               # for unstaffed pickup
  pickup_otp_required:            boolean, REQUIRED               # alternate to QR
  pickup_packaging:               STRICT ENUM, REQUIRED          # bag | box | tray
  pickup_packaging_thermal:       boolean, REQUIRED

OrderTrack (modified for pickup)

Same as base, REPLACE rider with:

pickup_ready:
  ready_iso:                      ISO_DATETIME, REQUIRED          # may equal future iso while preparing
  ready:                          boolean, REQUIRED
  qr_code_for_pickup_url:         URL, REQUIRED
  pickup_otp:                     string, REQUIRED                # may be empty if QR
  pickup_zone_address:            string, REQUIRED
  pickup_zone_lat:                float, REQUIRED
  pickup_zone_lng:                float, REQUIRED
  pickup_zone_kind:               STRICT ENUM, REQUIRED
  hold_time_remaining_minutes:    int, REQUIRED                  # before order discarded

REPLACE OrderTrack.status enum (see §5).

CartQuote (modified)

Same as base, delivery_fee_inr is FORCED to 0. ADD:

curbside_pickup_fee_inr:          INR_INTEGER, REQUIRED          # 0 typical
locker_pickup_fee_inr:            INR_INTEGER, REQUIRED          # 0 typical

Forbidden fields (additions)

fake_pickup_ready_iso | fake_curbside_pickup_supported |
fake_pickup_qr_code | hidden_pickup_zone_inflated_fee

5. CONTROLLED VOCABULARIES (DELTA)

All vocabularies from food.order_delivery §5 REQUIRED.

pickup_kind

user_walks_in_to_restaurant | curbside_pickup | locker_pickup |
drive_through | designated_pickup_zone

Restaurant.pickup_options.pickup_zone_kind and SearchResult.restaurant_pickup_zone

counter_at_entry | counter_at_back | curbside_zone | parking_lot |
ground_floor_locker | rooftop_pickup | drive_through_window

Restaurant.pickup_options.pickup_packaging

bag | box | tray | reusable_box | bagasse_box

OrderTrack.status (override)

pending_confirmation | accepted | preparing | almost_ready |
ready_for_pickup | user_arrived | user_picked_up | completed |
cancelled | expired_unpicked

extend_pickup_window.reason

traffic_delay | meeting_running_late | other

6. TTBS DIMENSIONS (DELTA)

All TIME / TASTE / BUDGET / SAFETY signals from food.order_delivery §6 REQUIRED. OVERRIDE:

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

Time stays high (must align with user arrival). Taste up (food at peak; no rider degradation). Budget unchanged. Safety drops (no rider risk, no handover risk).

TIME (override)

SIGNALS USED:
  - eta.cook_minutes ≤ pickup_window.start_iso − now    HARD FILTER (food ready in time)
  - distance_from_user_km                                weight 0.30
  - operating_hours.is_open_now                          HARD FILTER
  - eta.current_capacity_pct (lower=better)              weight 0.20
  - pickup_options.curbside_pickup_supported (if requested) weight 0.20
  - hold_time_remaining_minutes (longer=better)          weight 0.20
  - estimated_pickup_minutes match arrival_eta            weight 0.10

USER BAND HANDLING:
  - "I'll be there in 20 min" → tightens cook_minutes filter
  - "in next 1 hour" → relaxes

TASTE (override)

SIGNALS USED:
  - All base TASTE signals + amplification of fresh/hot:
  - Restaurant.fees.packaging.insulation (thermal=better) weight 0.10
  - Restaurant.fees.packaging.spill_proof_seal           weight 0.05
  - dish.preparation_method=tandoor/grilled (peak fresh) weight 0.05

SAFETY (override)

Drops to 0.10 weight; HARD FILTERS preserved (allergens, fssai, jain/halal/etc.).

Hidden ranking factor

information_completeness_score weight 0.10.


7. COMPLETION CONTRACT (DELTA)

Same shape as food.order_delivery §7. REPLACE delivered_at with picked_up_at. ADD pickup_window_used.

POST /api/v1/cpc/mcp_provider/{tomo_partner_id}
{
  "intent":            "food.order_pickup",
  "intent_version":    "v1.0.0",
  "external_id":       "ZOMATO-PKP-XYZ",
  "amount_inr":         580,
  "closed_at":         "2026-05-10T20:08:00+05:30",
  "request_id":        "req_pkp_8h2k_...",
  "status":            "picked_up",
  "order_ref":         "ZOMATO-PKP-XYZ",
  "merchant_id":       "ChIJxxxxx",
  "restaurant_id":     "zomato_rest_8423",
  "ready_at":          "2026-05-10T20:00:00+05:30",
  "picked_up_at":      "2026-05-10T20:08:00+05:30",
  "pickup_window_used": "scheduled_window",
  "extension_count":    0,
  "items_count":        2,
  "currency":          "INR",
  "fees_breakdown_total_inr": 30,
  "ratings_pending":     true,
  "notes":              ""
}

Status enum: picked_up | cancelled_by_user | cancelled_by_restaurant | expired_unpicked | failed


8. WIDGET (DELTA)

WIDGET TYPE:        food_pickup_results
SOURCE:             src/widgets/types.ts
TYPE NAME:          FoodPickupResultsPayload
RENDERED IN:        components/widgets/FoodPickupResultsWidget.tsx

Default: 3 surfaced restaurants with pickup-ready-eta + distance-from-user pill. Tap → menu drawer → cart → checkout → "Schedule pickup". Then PickupReadyCard with QR code + pickup zone address + hold-time countdown.


9. CACHING POLICY (DELTA)

Call TTL Rationale
confirm_user_pickup 0s always live
extend_pickup_window 0s always live
All others per food.order_delivery §9 inherited

10. ERROR CODES (DELTA)

All codes from food.order_delivery §10 REQUIRED. ADD:

Code HTTP Meaning TOMO behavior
PICKUP_WINDOW_INFEASIBLE 400 window doesn't allow cook time suggest later window
PICKUP_EXPIRED 410 order held past hold_time refund flow
QR_CODE_INVALID 400 QR rejected at scan surface
OTP_PICKUP_FAILED 401 OTP mismatch at counter surface
EXTEND_BLOCKED 409 partner cannot extend (next user reserved) surface

11. SANDBOX → PRODUCTION CHECKLIST (DELTA)

All checklist items from food.order_delivery §11 REQUIRED. ADD:

[ ] search_restaurants_and_dishes filters out non-pickup restaurants
[ ] pickup_options.curbside_pickup tested with real curbside zone
[ ] place_order returns qr_code_for_pickup_url
[ ] pickup_otp tested as QR alternate
[ ] confirm_user_pickup fires within 1min of QR scan
[ ] extend_pickup_window honors next-user reservation
[ ] hold_time_remaining_minutes accurately surfaced in track
[ ] Pickup zone GPS coords accurate
[ ] No hidden pickup zone fees
[ ] expired_unpicked refund flow tested

12. ANTI-FABRICATION RULES (DELTA)

All rules from food.order_delivery §12 REQUIRED. ADD:

RULE 11 — No fake pickup_ready_iso.
  ready_iso must be when food was physically packed and sealed for pickup.
  Marking ready when food is still cooking = breach.

RULE 12 — Curbside zone real and accessible.
  curbside_pickup_supported=true requires actual physical drop point reachable
  from a public road. Sending user to a closed back-door is breach.

RULE 13 — No fake hold_time_remaining_minutes.
  Hold time stated must be honored. Discarding food before stated end =
  breach + automatic refund tier.

RULE 14 — QR code real and decodable.
  qr_code_for_pickup_url must point to a working code that scans on partner
  devices. Fake placeholder = breach.

RULE 15 — No commission-based pickup_zone_inflated_fee.
  curbside_pickup_fee_inr cannot vary by partner commission rate.

RULE 16 — Extension cannot be silently denied.
  If extend_pickup_window returns success, partner MUST hold the order. Marking
  extended then discarding = breach.

VERSION HISTORY

v1.0.0 — 2026-05-10 — Initial spec (delta over food.order_delivery)