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.0auto.book_car_wash

Intent Spec — auto.book_car_wash

FULL ID:       auto.book_car_wash
VERSION:       v1.0.0
STATUS:        draft
LAST UPDATED:  2026-05-11
DOMAIN:        auto
PRIMARY AGENT: AutoServicesAgent
TTBS WEIGHTS:  time=0.35 taste=0.15 budget=0.30 safety=0.20

User books a car or two-wheeler wash — exterior, interior, or both. Covers basic foam wash, machine wash, doorstep wash, premium wash with polish. Excludes full detailing (coating, paint correction) which is its own intent (auto.book_detailing).

Partner exemplars: Exppress Car Wash, Wash Me Doorstep, GoMechanic Wash, Pitstop Wash, MyTVS Wash, neighbourhood multi-brand garages.


SECTION 1 — INTENT IDENTITY

This intent fires when a user wants their vehicle washed. Distinct from:

  • auto.book_detailing — coating, polish, paint correction, longer turnaround
  • auto.book_general_service — washing is a side-effect of service, not the goal
  • lifestyle.book_at_home_service — not vehicle-related

Single intent fires per booking. If user wants wash + service same day, multi-intent fan-out kicks in.


SECTION 2 — NATURAL LANGUAGE COVERAGE

CLASSIFIES IN

  • "Book a car wash"
  • "Need a car wash this evening"
  • "Doorstep car wash near me"
  • "Wash my bike tomorrow morning"
  • "Cheapest foam wash in Hyderabad"
  • "Premium car wash with interior cleaning"
  • "Get my Swift washed and vacuumed"
  • "Quick car wash in 30 minutes"
  • "Schedule a car wash at home"
  • "Wash and polish my car this Saturday"

CLASSIFIES OUT — BORDERLINE NO

  • "Full detailing for my car" → auto.book_detailing
  • "Service my car" → auto.book_general_service
  • "Paint touch-up" → auto.book_paint_job
  • "Tyres need cleaning" → ambiguous; if standalone → auto.book_car_wash, if alignment/rotation → auto.book_tyre_alignment

MULTI-INTENT TRIGGERS

  • "Service and wash on Saturday" → auto.book_general_service + auto.book_car_wash (same workshop preferred)
  • "Wash my car and then I need a ride to the airport" → auto.book_car_wash + mobility.book_airport_transfer

SECTION 3 — INPUT (TOMO → PROVIDER)

{
  "intent": "auto.book_car_wash",
  "request_id": "req_01J9Z...",
  "user_locale": "en-IN",
  "user_currency": "INR",
  "user_location": { "lat": 17.4475, "lng": 78.3563, "max_radius_km": 8, "city": "Hyderabad" },
  "vehicle": {
    "type": "car",                            // STRICT ENUM §6
    "size_class": "sedan",                    // STRICT ENUM §6
    "make": "Maruti Suzuki",
    "model": "Swift",
    "registration_number_last4": "1234"
  },
  "wash_preferences": {
    "wash_type": "premium",                   // STRICT ENUM §6, nullable for "anything"
    "include_interior": true,
    "include_polish": false,
    "preferred_window": {
      "start": "2026-05-13T16:00:00+05:30",
      "end":   "2026-05-13T19:00:00+05:30"
    },
    "doorstep_only": false,                   // true = filter to mobile providers only
    "max_duration_minutes": 60
  },
  "ttbs_user_band": {
    "time":   "fast",
    "taste":  "balanced",
    "budget": "ok",
    "safety": "balanced"
  },
  "session_context": {
    "tomo_session_id": "ses_01J9Z...",
    "user_dna_hash":   "dna_v3_a7c9..."
  }
}
Field Type Constraint Notes
intent string REQUIRED, STRICT ENUM Always auto.book_car_wash
request_id string REQUIRED, ULID Idempotency
vehicle.type enum REQUIRED car | two_wheeler
vehicle.size_class enum REQUIRED, STRICT ENUM §6 Pricing varies by size
vehicle.registration_number_last4 string REQUIRED, len 4 Privacy
wash_preferences.wash_type enum REQUIRED nullable, STRICT ENUM §6 When null, return all types
wash_preferences.include_interior bool REQUIRED Filter / pricing differentiator
wash_preferences.preferred_window.* string REQUIRED, ISO_DATETIME
wash_preferences.doorstep_only bool REQUIRED
wash_preferences.max_duration_minutes int REQUIRED, 15-240 Filter by typical_duration_minutes

Anti-fabrication preamble: Same as §3 of auto.book_general_service.


SECTION 4 — PROVIDER TOOLS

Tool 1: search_wash_slots

PURPOSE:      Return up to 20 wash slots
INPUT:        §3
OUTPUT:       array<WashSlot> per §5
SLA:          p50 ≤ 500ms, p95 ≤ 1500ms, p99 ≤ 3000ms
RATE LIMIT:   60 req/min
IDEMPOTENCY:  request_id; 60s cache
RETRY:        1 on 429 / 2 on 5xx

Tool 2: create_wash_booking

PURPOSE:      Confirm slot + price; lock provider availability
INPUT:        { request_id, slot_id, vehicle.*, address?, contact_phone }
OUTPUT:       WashBooking per §5
SLA:          p50 ≤ 1500ms, p95 ≤ 4000ms
RATE LIMIT:   30 req/min
IDEMPOTENCY:  request_id
RETRY:        TOMO does NOT retry create

Tool 3: cancel_wash_booking

PURPOSE:      Cancel pre-arrival; honour cancellation policy
INPUT:        { request_id, booking_id, reason_code }
OUTPUT:       CancellationResult
SLA:          p50 ≤ 600ms, p95 ≤ 1800ms
RATE LIMIT:   30 req/min
RETRY:        1 on 5xx

No get_quote tool because wash pricing is itemized inline in WashSlot.price. The two-call pattern (search → create) keeps friction low for a single-product intent.


SECTION 5 — RESPONSE SHAPE

WashSlot

WashSlot:
  slot_id: { type: string, constraint: REQUIRED }
  provider:
    provider_id: { type: string, constraint: REQUIRED }
    name: { type: string, constraint: REQUIRED }
    provider_type:
      type: enum
      constraint: REQUIRED, STRICT ENUM §6
      values: [workshop_bay, doorstep_mobile, fuel_station_attached, automated_tunnel]
    address: { type: string, constraint: REQUIRED }
    location: { type: object, shape: { lat, lng }, constraint: REQUIRED }
    distance_from_user_km: { type: float, constraint: REQUIRED, 0-30 }
    water_source:
      type: enum
      constraint: REQUIRED, STRICT ENUM §6
      values: [tap, recycled, bottled, dry_clean]
      semantics: TOMO surfaces "recycled" / "dry_clean" as sustainability badge

  slot_window:
    start: { type: string, constraint: REQUIRED, ISO_DATETIME }
    end: { type: string, constraint: REQUIRED, ISO_DATETIME }
    typical_duration_minutes: { type: int, constraint: REQUIRED, 15-240 }

  wash_type:
    code: { type: enum, constraint: REQUIRED, STRICT ENUM §6 }
    label: { type: string, constraint: REQUIRED }
    includes:
      type: array<string>
      constraint: REQUIRED, ≥2 entries
      example: ["exterior_foam", "tyre_shine", "interior_vacuum"]
    excludes:
      type: array<string>
      constraint: REQUIRED, may be empty
      example: ["under_chassis", "engine_bay"]

  price:
    base_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
    surcharge_inr:
      type: int
      constraint: REQUIRED, INR_INTEGER, ≥0
      semantics: extra for doorstep / oversize / out-of-hours
    gst_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
    total_inr: { type: int, constraint: REQUIRED, INR_INTEGER, base + surcharge + gst }
    fixed_price_guaranteed: { type: boolean, constraint: REQUIRED }

  logistics:
    user_present_required: { type: boolean, constraint: REQUIRED }
    drop_off_pickup_available: { type: boolean, constraint: REQUIRED }
    while_you_wait_acceptable: { type: boolean, constraint: REQUIRED }

  ratings:
    avg_rating: { type: float, constraint: REQUIRED, 0-5 }
    review_count: { type: int, constraint: REQUIRED, ≥0 }
    repeat_customer_pct_last_30d: { type: int, constraint: REQUIRED, 0-100 }

  partner_reference:
    source: { type: string, constraint: REQUIRED }
    deeplink: { type: string, constraint: REQUIRED, HTTPS URL }

WashBooking

WashBooking:
  booking_id: { type: string, constraint: REQUIRED, immutable }
  slot_id: { type: string, constraint: REQUIRED }
  scheduled_start: { type: string, constraint: REQUIRED, ISO_DATETIME }
  provider_name: { type: string, constraint: REQUIRED }
  contact_phone: { type: string, constraint: REQUIRED, E.164, partner-side dispatcher }
  arrival_eta: { type: string, constraint: REQUIRED nullable, ISO_DATETIME, required for doorstep_mobile }
  qr_or_code: { type: string, constraint: REQUIRED nullable, semantics: "for self-service tunnels only" }
  payment_due_at: { type: enum, constraint: REQUIRED, STRICT ENUM §6, values: [now, on_arrival, on_completion] }

CancellationResult

CancellationResult:
  booking_id: { type: string, constraint: REQUIRED }
  cancelled_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
  cancellation_fee_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
  refund_amount_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
  refund_eta_days: { type: int, constraint: REQUIRED, 0-7 }

FORBIDDEN FIELDS

  • paid_placement_score, ad_bid, sponsored_rank, promotion_priority
  • kickback_amount, referral_fee_kickback, _partner_revenue_share
  • artificial_urgency_text
  • ai_generated_photo for provider imagery
  • commission_padded_price

SECTION 6 — CONTROLLED VOCABULARIES

vehicle.size_class:
  values:
    hatchback: "Compact hatchback (e.g. Swift, i10)"
    sedan: "Sedan (e.g. City, Verna)"
    suv: "SUV / crossover (e.g. Creta, Seltos)"
    luv: "Large utility vehicle (e.g. Innova, Fortuner)"
    mpv: "Multi-purpose van"
    two_wheeler_small: "Up to 150cc bike / scooter"
    two_wheeler_large: "Above 150cc bike / cruiser"

wash_type.code:
  values:
    basic_exterior: "Foam + rinse exterior only"
    basic_full:     "Exterior + quick interior dust"
    premium:        "Premium foam + interior vacuum + tyre shine"
    polish:         "Premium + machine polish"
    interior_deep:  "Interior deep clean only (no exterior)"
    dry_clean:      "Waterless detailing wipe-down"

provider_type:
  values:
    workshop_bay:            "Brick-and-mortar workshop wash bay"
    doorstep_mobile:         "Mobile crew at user's location"
    fuel_station_attached:   "Wash bay attached to fuel station"
    automated_tunnel:        "Drive-through automated tunnel"

water_source:
  values:
    tap:       "Standard tap water"
    recycled:  "Water reclaimed from previous washes (sustainability)"
    bottled:   "Bottled / filtered (premium)"
    dry_clean: "Waterless"

payment_due_at:
  values:
    now:          "Pay at booking"
    on_arrival:   "Pay when crew arrives or vehicle dropped"
    on_completion: "Pay after wash done"

SECTION 7 — TTBS DIMENSIONS

TIME (weight = 0.35):
  signals_used:
    - provider.distance_from_user_km (for non-doorstep)
    - slot_window.start vs user's window center
    - typical_duration_minutes vs max_duration_minutes
  weighting:
    distance: 0.30
    fit: 0.40
    duration: 0.30
  user_band_handling:
    fast: prefer slots in next 60min; relax distance
    balanced: standard
    flexible: any in window

TASTE (weight = 0.15):
  signals_used:
    - provider_type matching user DNA history
    - wash_type.code matching declared preference
    - water_source sustainability (recycled / dry_clean bonus for sustainability-leaning DNA)
  weighting:
    type_match: 0.40
    wash_match: 0.40
    sustainability: 0.20
  user_band_handling: standard across bands

BUDGET (weight = 0.30):
  signals_used:
    - price.total_inr
    - price.fixed_price_guaranteed
  weighting:
    price: 0.75
    lock_bonus: 0.25
  user_band_handling:
    ok: prefer cheapest basic_exterior
    good: balance price vs wash_type
    great: prefer premium / polish even at 3x base

SAFETY (weight = 0.20):
  signals_used:
    - ratings.avg_rating
    - ratings.repeat_customer_pct_last_30d
    - logistics.user_present_required (false = lower trust requirement)
  weighting:
    rating: 0.50
    repeat: 0.30
    presence_bonus: 0.20  # if user doesn't have to be present, slight bonus
  user_band_handling:
    fast: relax repeat threshold
    balanced: standard
    flexible: prefer high-trust providers (high repeat %)

SECTION 8 — COMPLETION CONTRACT

POST /api/v1/cpc/mcp_provider/<your_partner_id>
Body:
{
  "intent":           "auto.book_car_wash",
  "external_id":      "<booking_id>",
  "request_id":       "<request_id>",
  "amount_inr":       250,       // NET (base + supplier-kept surcharge)
  "gst_inr":          45,        // government — excluded
  "tips_inr":         50,        // tip to wash crew — excluded (driver/staff money)
  "pass_through_inr": 0,
  "closed_at":        "2026-05-13T17:35:00+05:30",
  "status":           "completed",  // STRICT ENUM: completed | cancelled_by_user | cancelled_by_partner | no_show
  "wash_type":        "premium"
}

HMAC over ${timestamp}.${body}. 5-min replay. 10% × amount_inr is commission (NET-only).


SECTION 9 — WIDGET

AutoWashWidget (planned in src/widgets/AutoWashWidget.tsx). Interim: generic ListingsWidget.

Field mapping:
  - WashSlot.provider.name → header
  - WashSlot.provider_type + distance_from_user_km → subline 1
  - WashSlot.slot_window.start + typical_duration_minutes → subline 2
  - WashSlot.price.total_inr → price row
  - WashSlot.water_source = "recycled" or "dry_clean" → sustainability pill (green)
  - WashSlot.ratings.avg_rating → rating badge

L2/L3 deeplinks inline; L1 fallback per locked policy.


SECTION 10 — CACHING POLICY

Call TTL Rationale
search_wash_slots 30s Wash slots churn faster than service slots
create_wash_booking NO CACHE Idempotent by request_id
cancel_wash_booking NO CACHE
Provider static metadata 24h Static

SECTION 11 — ERROR CODES

Code HTTP Meaning Retry
INVALID_REQUEST 400 Payload malformed No
INVALID_AUTH 401 Bad creds No
RATE_LIMITED 429 Throttle 1, 2s
INTERNAL_ERROR 500 Partner failure 2, exp
IDEMPOTENCY_VIOLATION 409 request_id reused No
SIGNATURE_INVALID 401 (webhook) HMAC fail No
VEHICLE_TOO_LARGE 422 size_class outside provider's accepted No
NO_SLOTS_IN_WINDOW 200 (empty) Valid, no matches n/a
SLOT_GONE 409 (create) Slot booked between search and create No; UI re-prompts
PROVIDER_OFFLINE 422 Provider not currently operating No
DOORSTEP_UNAVAILABLE_AT_LOCATION 422 User location outside doorstep service area No
CANCELLATION_FEE_DUE 200 (cancel) Non-zero cancellation_fee n/a

SECTION 12 — SANDBOX → PRODUCTION CHECKLIST

[ ] All three tools (search/create/cancel) implemented
[ ] 5+ real providers + 10+ slots in 24h window
[ ] vehicle.size_class pricing differentiated correctly (hatchback < sedan < SUV < LUV)
[ ] All wash_type codes return non-empty includes[] array
[ ] HMAC signing verified
[ ] amount_inr is NET in CPC payload
[ ] tips_inr field correctly captures user-paid tips (not zero by default)
[ ] No forbidden fields anywhere
[ ] SLA p95 ≤ 1500ms on search
[ ] Idempotency tested
[ ] Doorstep providers return arrival_eta in WashBooking
[ ] water_source claims verifiable (TOMO inspects on-site for "recycled" claim)
[ ] Compliance docs: GSTIN, shop license, privacy policy

SECTION 13 — ANTI-FABRICATION RULES

RULE 1: No paid_placement / ad / kickback fields. Rejects entire response.

RULE 2: price.fixed_price_guaranteed=true means total_inr is the final amount.
        Partners who upsell on-arrival above fixed_price face suspension after
        3 incidents (verified via CPC amount_inr vs slot total_inr).

RULE 3: water_source claim of "recycled" or "dry_clean" must be verifiable.
        TOMO field-tests on randomly selected bookings; mismatches result in
        compliance review and water_source claim revocation.

RULE 4: ratings.repeat_customer_pct_last_30d must be computed from real
        booking data, not aspirational. TOMO cross-checks via its own
        completion ledger for that provider.

RULE 5: typical_duration_minutes must reflect actual median completion time
        for the wash_type. Sustained mismatch >50% triggers review.

RULE 6: No fast_selling / artificial_urgency in any subline text.

RULE 7: Photos of provider must be real. AI-generated forbidden.

RULE 8: provider_type=automated_tunnel must actually be automated — partners
         can't tag manual wash as "automated" to game TIME ranking.

RULE 9: includes[] array must reflect actual service performed. Partners
         who skip listed steps (e.g. promise interior_vacuum but skip it)
         face suspension; verified via customer post-wash reports.

RULE 10: No "Top Rated" / "Editor's Choice" badges; TTBS ranks source-blind.

VERSION HISTORY

v1.0.0 — 2026-05-11 — Initial spec. NET commission base in §8.