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_addresswithpickup_windowandpickup_kind - OUTPUT: replaces
rider_assignment_eta_minuteswithpickup_ready_isoandqr_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)