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_general_service

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 context
  • auto.book_major_service — engine overhaul, transmission, accident-damage repair (higher cost band, longer turnaround)
  • auto.book_battery_replacement — single-part replacement, no inspection
  • auto.book_tyre_alignment — single-service intent, no other work
  • auto.book_paint_job — cosmetic-only
  • mobility.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_priority
  • kickback_amount, referral_fee_kickback, _partner_revenue_share
  • artificial_urgency_text (or value strings like "Hurry! Only 1 slot left!" without numeric backing)
  • ai_generated_photo for workshop imagery
  • commission_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.