Intent Spec — auto.book_general_service
FULL ID: auto.book_general_service
VERSION: v1.0.0
STATUS: draft
LAST UPDATED: 2026-05-11
DOMAIN: auto
PRIMARY AGENT: AutoServicesAgent
TTBS WEIGHTS: time=0.20 taste=0.20 budget=0.30 safety=0.30
User books periodic / scheduled service for their vehicle (car or two-wheeler) at an authorised workshop, multi-brand garage, or doorstep mechanic. Covers oil change, filter replacement, brake check, fluid top-up, generic 10K/20K-km service intervals — anything the user calls "regular service," "periodic service," "scheduled maintenance," or "service my car."
Partner exemplars: GoMechanic, Pitstop, Bumper, MyTVS, Carnation, manufacturer authorised service centres (Maruti Suzuki Authorised, Tata Motors Workshop, Honda Workshop, etc.), Park+, Spinny Workshops.
SECTION 1 — INTENT IDENTITY
This intent fires when a user wants a scheduled / preventive service on their existing vehicle. It is distinct from:
auto.book_breakdown_assist— vehicle is currently non-functional, emergency contextauto.book_major_service— engine overhaul, transmission, accident-damage repair (higher cost band, longer turnaround)auto.book_battery_replacement— single-part replacement, no inspectionauto.book_tyre_alignment— single-service intent, no other workauto.book_paint_job— cosmetic-onlymobility.book_self_drive— user is renting, not servicing
Single intent fires per booking even if the partner upsells additional services during workshop visit. Upsells are handled in the completion payload (§8), not as separate intents.
SECTION 2 — NATURAL LANGUAGE COVERAGE
CLASSIFIES IN (min. 8)
- "Book a service for my car"
- "Need to get my Swift serviced this Saturday"
- "Oil change due, find me a workshop near home"
- "10000 km service for my Honda City"
- "Where can I get my bike serviced cheap"
- "Book a periodic service at the nearest authorised centre"
- "My Activa needs servicing — any doorstep options?"
- "I want to drop my car for service tomorrow morning"
- "Get me a service slot at GoMechanic"
- "Annual maintenance for my Hyundai Creta"
CLASSIFIES OUT — BORDERLINE NO
- "My car broke down on ORR" →
auto.book_breakdown_assist - "Replace my battery" →
auto.book_battery_replacement - "Engine making noise, may need overhaul" →
auto.book_major_service - "Just a car wash today" →
auto.book_car_wash - "RC transfer help" →
auto.book_rc_transfer - "Insurance renewal" →
auto.book_insurance_renewal - "Buy a new car" → not a TOMO intent yet (v2:
auto.buy_new_vehicle)
MULTI-INTENT TRIGGERS (fan-out)
- "Book a service and a car wash on Saturday" →
auto.book_general_service+auto.book_car_wash(sequenced at same workshop where possible) - "Service the car and renew insurance" →
auto.book_general_service+auto.book_insurance_renewal - "Drop the car for service and book me a ride home" →
auto.book_general_service+mobility.book_intracity_ride(with workshop drop-off as ride pickup)
SLM dataset entries are tagged with both intent IDs and temporal relationship (AT_SAME_VENUE, AFTER, BEFORE).
SECTION 3 — INPUT (TOMO → PROVIDER)
{
"intent": "auto.book_general_service",
"request_id": "req_01J9Z...",
"user_locale": "en-IN",
"user_currency": "INR",
"user_location": {
"lat": 17.4475,
"lng": 78.3563,
"max_radius_km": 12,
"city": "Hyderabad"
},
"vehicle": {
"type": "car", // STRICT ENUM §6: car | two_wheeler
"make": "Maruti Suzuki",
"model": "Swift",
"variant": "VXi", // optional but recommended
"fuel_type": "petrol", // STRICT ENUM §6
"year_of_manufacture": 2021, // 1990-current year
"registration_number_last4": "1234", // last 4 digits only; never full plate
"current_odometer_km": 42500, // user-declared; partner may verify on intake
"last_service_odometer_km": 32500 // null when user doesn't know
},
"service_preferences": {
"service_type_hint": "scheduled_10k", // STRICT ENUM §6, nullable
"preferred_window": {
"start": "2026-05-13T09:00:00+05:30",
"end": "2026-05-13T18:00:00+05:30"
},
"drop_off_pickup_required": true,
"doorstep_service_acceptable": true, // accept workshop OR doorstep-mechanic options
"authorised_only": false // true = only OEM-authorised; false = multi-brand acceptable
},
"ttbs_user_band": {
"time": "balanced",
"taste": "balanced",
"budget": "good",
"safety": "good"
},
"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_general_service |
request_id |
string | REQUIRED, ULID | Idempotency key |
vehicle.type |
enum | REQUIRED, STRICT ENUM §6 | |
vehicle.make / model |
string | REQUIRED | Partner must surface full normalized brand catalog |
vehicle.fuel_type |
enum | REQUIRED, STRICT ENUM §6 | petrol / diesel / cng / electric / hybrid |
vehicle.year_of_manufacture |
int | REQUIRED, 1990-2026 | Out-of-range → INVALID_REQUEST |
vehicle.registration_number_last4 |
string | REQUIRED, length 4, digits/letters | Privacy: never full plate from TOMO |
vehicle.current_odometer_km |
int | REQUIRED, ≥0 | Drives service interval scheduling |
service_preferences.preferred_window.* |
string | REQUIRED, ISO_DATETIME | Partner returns shows fully within window |
service_preferences.drop_off_pickup_required |
bool | REQUIRED | Partner MUST honour |
service_preferences.doorstep_service_acceptable |
bool | REQUIRED | Filter on workshop_type when false |
service_preferences.authorised_only |
bool | REQUIRED | Filter on partnership_type when true |
Anti-fabrication preamble: TOMO will never inject paid_placement signals, urgency text, or commission-influenced fields. Provider may not reject the request based on TOMO's commission rate, vehicle age, or refuse-to-quote for budget bands.
SECTION 4 — PROVIDER TOOLS
Tool 1: search_service_slots
PURPOSE: Return up to 20 service slots matching window + location + vehicle + preferences
INPUT: §3 above
OUTPUT: array<ServiceSlot> per §5
SLA: p50 ≤ 800ms, p95 ≤ 2000ms, p99 ≤ 4000ms
RATE LIMIT: 60 req/min per partner
IDEMPOTENCY: request_id; cached 60s
RETRY: 1 retry on RATE_LIMITED (2s backoff); 2 retries on 5xx (exp backoff)
Tool 2: get_service_quote
PURPOSE: Return itemized quote for a specific slot — labour + parts + GST
INPUT: { request_id, slot_id, vehicle.* }
OUTPUT: ServiceQuote per §5
SLA: p50 ≤ 600ms, p95 ≤ 1800ms
RATE LIMIT: 60 req/min per partner
IDEMPOTENCY: slot_id (within 5-min window)
RETRY: 1 retry on 5xx
Tool 3: create_service_booking
PURPOSE: Confirm the slot; lock workshop bay; trigger any pickup logistics
INPUT: { request_id, slot_id, quote_id, vehicle.*, pickup_address?, contact_phone }
OUTPUT: ServiceBooking per §5
SLA: p50 ≤ 2000ms, p95 ≤ 5000ms
RATE LIMIT: 30 req/min per partner
IDEMPOTENCY: request_id; same ID returns same booking
RETRY: TOMO does NOT retry create_service_booking
Tool 4: cancel_service_booking
PURPOSE: Cancel a confirmed booking pre-intake; honour partner cancellation policy
INPUT: { request_id, booking_id, reason_code }
OUTPUT: CancellationResult per §5
SLA: p50 ≤ 800ms, p95 ≤ 2000ms
RATE LIMIT: 30 req/min per partner
RETRY: 1 retry on 5xx
SECTION 5 — RESPONSE SHAPE
ServiceSlot (returned by search_service_slots)
ServiceSlot:
slot_id: { type: string, constraint: REQUIRED, opaque, partner-namespaced }
workshop:
workshop_id: { type: string, constraint: REQUIRED }
name: { type: string, constraint: REQUIRED }
workshop_type:
type: enum
constraint: REQUIRED, STRICT ENUM §6
values: [oem_authorised, multi_brand, doorstep_mobile]
partnership_type:
type: enum
constraint: REQUIRED, STRICT ENUM §6
values: [oem_direct, oem_authorised, independent]
address: { type: string, constraint: REQUIRED }
location: { type: object, shape: { lat, lng }, constraint: REQUIRED }
distance_from_user_km: { type: float, constraint: REQUIRED, 0-50 }
accreditations:
type: array<enum>
constraint: REQUIRED, may be empty
values: [iso_9001, oem_certified, msme_registered]
bay_capacity: { type: int, constraint: REQUIRED, 1-50 }
typical_completion_hours: { type: int, constraint: REQUIRED, 1-72 }
slot_window:
start: { type: string, constraint: REQUIRED, ISO_DATETIME }
end: { type: string, constraint: REQUIRED, ISO_DATETIME }
estimated_completion: { type: string, constraint: REQUIRED, ISO_DATETIME }
service_type:
code:
type: enum
constraint: REQUIRED, STRICT ENUM §6
values: [scheduled_5k, scheduled_10k, scheduled_15k, scheduled_20k,
scheduled_30k, scheduled_40k, scheduled_60k, scheduled_80k,
scheduled_100k, generic_inspection]
label: { type: string, constraint: REQUIRED, locale-aware human label }
includes:
type: array<string>
constraint: REQUIRED, ≥3 entries
example: ["engine_oil", "oil_filter", "air_filter_clean", "brake_check", "fluid_topup"]
estimated_price:
base_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
labour_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
parts_inr_estimate: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0, NOTE: "actual parts billed on intake" }
gst_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
total_estimate_inr: { type: int, constraint: REQUIRED, INR_INTEGER, MUST equal base + labour + parts + gst }
price_lock_guaranteed: { type: boolean, constraint: REQUIRED }
price_lock_variance_cap_pct: { type: int, constraint: REQUIRED, 0-50, semantics: "max % parts can exceed estimate without re-approval" }
logistics:
drop_off_pickup_available: { type: boolean, constraint: REQUIRED }
drop_off_pickup_fee_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0, semantics: "0 if free; otherwise itemized" }
doorstep_available: { type: boolean, constraint: REQUIRED }
while_you_wait_acceptable: { type: boolean, constraint: REQUIRED }
warranty:
parts_warranty_months: { type: int, constraint: REQUIRED, 0-60 }
labour_warranty_months: { type: int, constraint: REQUIRED, 0-12 }
warranty_terms_url: { type: string, constraint: REQUIRED, HTTPS URL }
ratings:
avg_rating: { type: float, constraint: REQUIRED, 0-5, semantics: "unrounded, raw" }
review_count: { type: int, constraint: REQUIRED, ≥0 }
last_30day_completion_rate_pct: { type: int, constraint: REQUIRED, 0-100 }
partner_reference:
source: { type: string, constraint: REQUIRED }
deeplink: { type: string, constraint: REQUIRED, HTTPS URL }
ServiceQuote (returned by get_service_quote)
ServiceQuote:
quote_id: { type: string, constraint: REQUIRED, opaque }
slot_id: { type: string, constraint: REQUIRED, matches §5 slot_id }
validity_until: { type: string, constraint: REQUIRED, ISO_DATETIME, ≤30min from issue }
line_items:
type: array<LineItem>
constraint: REQUIRED, ≥1
shape:
sku: { type: string, constraint: REQUIRED, opaque, partner-namespaced }
description: { type: string, constraint: REQUIRED, locale-aware }
category: { type: enum, constraint: REQUIRED, STRICT ENUM §6 }
quantity: { type: int, constraint: REQUIRED, ≥1 }
unit_price_inr: { type: int, constraint: REQUIRED, INR_INTEGER }
total_inr: { type: int, constraint: REQUIRED, INR_INTEGER, quantity × unit_price }
optional: { type: boolean, constraint: REQUIRED, semantics: "user may decline this line" }
totals:
subtotal_inr: { type: int, constraint: REQUIRED, INR_INTEGER }
discount_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
gst_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
total_inr: { type: int, constraint: REQUIRED, INR_INTEGER }
ServiceBooking (returned by create_service_booking)
ServiceBooking:
booking_id: { type: string, constraint: REQUIRED, opaque, immutable }
slot_id: { type: string, constraint: REQUIRED }
workshop_name: { type: string, constraint: REQUIRED }
scheduled_start: { type: string, constraint: REQUIRED, ISO_DATETIME }
estimated_completion: { type: string, constraint: REQUIRED, ISO_DATETIME }
pickup_arranged: { type: boolean, constraint: REQUIRED }
pickup_eta: { type: string, constraint: REQUIRED nullable when pickup_arranged=false, ISO_DATETIME }
service_advisor_name: { type: string, constraint: REQUIRED }
service_advisor_phone: { type: string, constraint: REQUIRED, E.164 }
payment_due_at: { type: enum, constraint: REQUIRED, STRICT ENUM §6, values: [intake, completion, weekly_invoice] }
partner_booking_reference: { type: string, constraint: REQUIRED }
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-14 }
FORBIDDEN FIELDS
paid_placement_score,ad_bid,sponsored_rank,promotion_prioritykickback_amount,referral_fee_kickback,_partner_revenue_shareartificial_urgency_text(or value strings like "Hurry! Only 1 slot left!" without numeric backing)ai_generated_photofor workshop imagerycommission_padded_price
SECTION 6 — CONTROLLED VOCABULARIES
vehicle.type:
values:
car: "Four-wheeler passenger vehicle"
two_wheeler: "Motorcycle / scooter / moped"
vehicle.fuel_type:
values:
petrol: "Petrol / gasoline"
diesel: "Diesel"
cng: "Compressed natural gas"
electric: "Battery electric"
hybrid: "Petrol-electric or diesel-electric hybrid"
service_type.code:
values:
scheduled_5k: "5,000 km / 1st service"
scheduled_10k: "10,000 km service"
scheduled_15k: "15,000 km service"
scheduled_20k: "20,000 km service"
scheduled_30k: "30,000 km service"
scheduled_40k: "40,000 km service"
scheduled_60k: "60,000 km service"
scheduled_80k: "80,000 km service"
scheduled_100k: "100,000 km service"
generic_inspection: "General inspection, no scheduled interval declared"
workshop_type:
values:
oem_authorised: "Brand's authorised workshop"
multi_brand: "Independent multi-brand garage"
doorstep_mobile: "Mobile mechanic at user's location"
partnership_type:
values:
oem_direct: "Brand-owned"
oem_authorised: "Brand-licensed independent"
independent: "Fully independent, no brand affiliation"
line_item.category:
values:
labour: "Mechanic time"
consumable: "Engine oil, fluids, etc."
part: "Replaced part (filter, plug, belt, etc.)"
inspection: "Diagnostic / inspection step"
pickup_drop: "Pickup-and-drop logistics fee"
addon: "Optional upsell (paint touch-up, polish, etc.)"
payment_due_at:
values:
intake: "User pays at drop-off / before work begins"
completion: "User pays on pickup"
weekly_invoice: "Workshop invoices weekly (corporate accounts)"
Vocabulary changes require v1.x bump and 30-day notice.
SECTION 7 — TTBS DIMENSIONS
TIME (weight = 0.20):
signals_used:
- workshop.distance_from_user_km
- slot_window.start (proximity to user's window center)
- slot.typical_completion_hours
weighting:
distance: 0.30
slot_fit: 0.40
completion_speed: 0.30
user_band_handling:
fast: prefer slots completing in ≤4h; relax distance
balanced: standard weights
flexible: any slot in window acceptable
TASTE (weight = 0.20):
signals_used:
- workshop.partnership_type vs user DNA preference (OEM-loyal vs price-conscious)
- workshop_type matching user history
- ratings.avg_rating
weighting:
partnership_fit: 0.40
type_match: 0.30
rating: 0.30
user_band_handling:
fast: not applicable
balanced: standard
flexible: widen tolerance for non-preferred partnership types
BUDGET (weight = 0.30):
signals_used:
- estimated_price.total_estimate_inr
- estimated_price.price_lock_guaranteed
- logistics.drop_off_pickup_fee_inr
weighting:
total_price: 0.60
price_lock_bonus: 0.25 # 25% rank bonus when price_lock_guaranteed=true
logistics_fee: 0.15
user_band_handling:
ok: prefer cheapest; relax workshop_type to multi_brand
good: balance price vs OEM-authorised vs ratings
great: prefer OEM-direct + price_lock_guaranteed even at higher cost
SAFETY (weight = 0.30):
signals_used:
- workshop.accreditations
- ratings.last_30day_completion_rate_pct
- warranty.parts_warranty_months
- warranty.labour_warranty_months
weighting:
accreditations: 0.30
completion_rate: 0.30
warranty: 0.40
user_band_handling:
fast: relax warranty thresholds
balanced: standard
flexible: prefer high-warranty workshops even if slower
Locked in server/lib/domain-agent-map.ts under domain auto. Partner cannot influence weights.
SECTION 8 — COMPLETION CONTRACT
When service is completed and vehicle handed back (or terminal failure), partner POSTs:
POST https://www.automobnxt.com/api/v1/cpc/mcp_provider/<your_partner_id>
Headers: Content-Type, X-TOMO-Timestamp, X-TOMO-Signature: sha256=<hex>
Body:
{
"intent": "auto.book_general_service",
"external_id": "<booking_id>",
"request_id": "<request_id>",
"amount_inr": 2800, // NET supplier revenue = labour + supplier-margin on parts
"gst_inr": 504, // GST passed to government — EXCLUDED from commission
"tips_inr": 0, // none in auto service typically
"pass_through_inr": 0, // any third-party fee partner doesn't keep (e.g. towing sub-contractor)
"closed_at": "2026-05-13T17:42:00+05:30",
"status": "completed", // STRICT ENUM: completed | cancelled_by_user | cancelled_by_partner | no_show | partial_service
"service_type": "scheduled_10k",
"upsells_inr": 0 // additional services accepted on-site (also NET-only)
}
HMAC over ${X-TOMO-Timestamp}.${rawBodyJSON} using webhook_signing_key. 5-min replay window.
10% × amount_inr is commission. Not 10% × gross. Locked by founder doctrine 2026-05-11 (NET commission base).
SECTION 9 — WIDGET
Rendered via AutoServiceWidget in src/widgets/AutoServiceWidget.tsx (planned). Falls back to generic ListingsWidget interim.
Source: src/widgets/types.ts → AutoServicePayload
Field mapping:
- ServiceSlot.workshop.name → header
- ServiceSlot.workshop_type + distance_from_user_km → subline 1
- ServiceSlot.slot_window.start (formatted) + typical_completion_hours → subline 2
- ServiceSlot.estimated_price.total_estimate_inr + price_lock_guaranteed → price row
- ServiceSlot.ratings.avg_rating + review_count → rating badge
- ServiceSlot.warranty.parts_warranty_months → "Xm parts warranty" pill
L2/L3 partners deeplink inline; L1 fallback opens in app handoff (per locked L1→L2 doctrine).
SECTION 10 — CACHING POLICY
| Call | TTL | Rationale |
|---|---|---|
search_service_slots |
60s | Slot availability shifts slowly during business hours |
get_service_quote |
NO CACHE | Must reflect live parts pricing + labour rate |
create_service_booking |
NO CACHE | Idempotent by request_id; never cached |
cancel_service_booking |
NO CACHE | Same |
| Workshop static metadata (address, accreditations, warranty terms) | 24h | Static |
Failures NEVER cache.
SECTION 11 — ERROR CODES
| Code | HTTP | Meaning | TOMO retry |
|---|---|---|---|
INVALID_REQUEST |
400 | Payload malformed | No |
INVALID_AUTH |
401 | Bad credentials | No |
RATE_LIMITED |
429 | Partner throttle | 1, 2s backoff |
INTERNAL_ERROR |
500 | Partner failure | 2, exp backoff |
IDEMPOTENCY_VIOLATION |
409 | request_id reused with different payload | No |
SIGNATURE_INVALID |
401 (webhook) | HMAC mismatch | No; alert |
VEHICLE_NOT_SERVICEABLE |
422 | Make/model/year outside partner catalog | No |
NO_SLOTS_IN_WINDOW |
200 (empty) | Valid response, no matches | n/a |
SLOT_GONE |
409 (create) | Slot booked between search and create | No; UI re-prompts |
QUOTE_EXPIRED |
410 (create) | quote.validity_until passed | No; UI re-quotes |
WORKSHOP_CLOSED |
422 | Slot during partner downtime / holiday | No |
CANCELLATION_FEE_DUE |
200 (cancel) | Returns non-zero cancellation_fee_inr | n/a |
Free-text errors forbidden.
SECTION 12 — SANDBOX → PRODUCTION CHECKLIST
[ ] All four tools implemented and return shapes per §5
[ ] At least 3 real workshops + 10 real slots in 24h window populated
[ ] vehicle catalog covers major Indian makes/models (Maruti, Hyundai, Tata, Honda, Toyota, Kia, Mahindra, two-wheeler: Hero, Honda, TVS, Bajaj, Royal Enfield, Yamaha)
[ ] All controlled vocabularies respected
[ ] HMAC signing verified on test CPC webhook
[ ] amount_inr is NET in CPC payload (verified against partner's invoice breakdown)
[ ] gst_inr / pass_through_inr fields populated correctly (not zero by default)
[ ] No forbidden fields in 100+ sandbox responses
[ ] SLA p95 met: search ≤2000ms, quote ≤1800ms, create ≤5000ms
[ ] Idempotency: same request_id returns same booking; differing payload rejects
[ ] price_lock_variance_cap_pct honoured in test where parts exceed estimate
[ ] Warranty terms_url returns live HTTPS page
[ ] Compliance docs: GSTIN, shop license (state-specific), privacy policy URL live
[ ] Service advisor contact verified (phone reachable during work hours)
SECTION 13 — ANTI-FABRICATION RULES
RULE 1: No paid_placement / ad / sponsored / kickback fields. Single occurrence
rejects entire response. TOMO scans field names + semantic equivalents.
RULE 2: price_lock_guaranteed=true MUST mean parts cannot exceed estimate by
more than price_lock_variance_cap_pct. Partners who breach this on
completed bookings (verified via CPC amount vs quote totals) face
suspension after 3 incidents.
RULE 3: estimated_price.parts_inr_estimate must be a real estimate based on
the vehicle's known service interval, not a flat "₹500" placeholder.
TOMO cross-checks variance between estimate and actual on completed
bookings; sustained >40% deviation indicates fabrication.
RULE 4: ratings.avg_rating must be an unrounded float from raw reviews.
No "rounded up to 4.5 for marketing." TOMO samples recent reviews
and recomputes; deviation >0.1 flags review.
RULE 5: workshop.accreditations claimed (iso_9001, oem_certified) must be
verifiable. TOMO requires uploaded certificate during compliance review.
RULE 6: typical_completion_hours must reflect actual median historical
completion, not aspirational marketing time. TOMO measures booking-
created to completion-webhook delta and compares.
RULE 7: artificial_urgency_text forbidden. fast_selling-style scarcity claims
in subline copy not allowed; let widget surface based on capacity.
RULE 8: No "Top Pick" / "TOMO Recommended" badges in any field — ranking is
TTBS-driven, partner cannot self-badge.
RULE 9: Photos of the workshop must be real photos. AI-generated workshop
imagery forbidden; TOMO field-tests with reverse image search on
a sample.
RULE 10: warranty.parts_warranty_months and labour_warranty_months MUST be
honoured. If partner refuses warranty service on a TOMO-routed
booking after the fact, that's a status: suspended trigger.
VERSION HISTORY
v1.0.0 — 2026-05-11 — Initial spec. NET commission base reflected in §8 CPC payload.