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.0lifestyle.book_salon

lifestyle.book_salon — Full Intent Specification

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

Salon booking is the canonical lifestyle slot-based service intent. Distinct from food/mobility shape: (a) service_catalog is the listing unit, not inventory or vehicle — each service has duration + price + skill level; (b) stylist (or therapist/trainer in sibling lifestyle intents) is a first-class entity with skill and rating; (c) booking is slot + service + stylist; (d) safety drops to 0.10 (low-risk personal grooming with hygiene HARD FILTERS); (e) taste rises to 0.40 — outcome quality (haircut, color) is the experience.

This spec sets the SLOT-BASED SERVICE shape that the other 7 lifestyle specs (gym session, yoga, personal trainer, spa, gym membership, wellness retreat, at-home service) inherit and extend.


1. NATURAL LANGUAGE COVERAGE

Classifies IN

  • "haircut tomorrow morning"
  • "salon for haircut + facial"
  • "ladies salon for hair color"
  • "men's barber shop near me"
  • "book Naturals salon Saturday"
  • "balayage hair color appointment"
  • "manicure and pedicure"
  • "salon for bridal makeup trial"
  • "men's grooming package"

Classifies OUT — borderline NO

  • "spa treatment for stress relief" → lifestyle.book_spa_treatment
  • "yoga class" → lifestyle.book_yoga_class
  • "personal trainer" → lifestyle.book_personal_trainer
  • "gym session" → lifestyle.book_gym_session
  • "at-home haircut" → lifestyle.book_at_home_service

MULTI-INTENT TRIGGERS

  • "salon Saturday and book a cab" → lifestyle.book_salon + mobility.book_intracity_ride
  • "haircut + spa" → lifestyle.book_salon + lifestyle.book_spa_treatment
  • "salon and lunch reservation" → lifestyle.book_salon + food.book_dine_in

2. INPUT — TOMO → PROVIDER

{
  "intent":          "lifestyle.book_salon",
  "intent_version":  "v1.0.0",
  "request_id":      "req_sln_8h2k_2026-05-10T08:00:00Z",
  "user_session_id": "anon_user_token_or_uid",

  "search_area": {
    "lat":            17.4435,
    "lng":            78.3772,
    "radius_km":      5,
    "city":           "Hyderabad",
    "state_code":     "TS",
    "country_code":   "IN"
  },

  "scheduled_for_iso":   "2026-05-12T11:00:00+05:30",
  "duration_minutes_estimated": 90,

  "user_profile": {
    "gender":              "female",
    "age_band":            "26-35",
    "skin_kind":           "combination",
    "hair_kind":           "wavy",
    "hair_length":         "shoulder",
    "current_color":       "natural_black"
  },

  "services_wanted": [
    {
      "service_kind":         "hair_color_balayage",
      "service_duration_estimate": 75,
      "preferred_stylist_skill_min": 4
    },
    {
      "service_kind":         "haircut_styling",
      "service_duration_estimate": 30,
      "preferred_stylist_skill_min": 3
    }
  ],

  "preferences": {
    "salon_kind_acceptable":      ["unisex", "ladies"],
    "salon_kind_preferred":       "ladies",
    "stylist_gender_preference":  "female",
    "stylist_min_experience_years": 3,
    "stylist_certifications_required": false,
    "stylist_kyc_required":       true,
    "products_kind_preferred":    ["loreal", "schwarzkopf", "matrix"],
    "ammonia_free_color_required": true,
    "vegan_products_only":        false,
    "halal_products_required":    false,
    "budget_band":                "good",
    "budget_max_inr_total":        4500,
    "lounge_amenity_required":    ["air_conditioning", "music"],
    "atmosphere_kind":            "calm",
    "max_wait_minutes_at_salon":   15,
    "online_payment_supported_required": true
  },

  "context": {
    "user_locale":         "en-IN",
    "user_currency_pref":  "INR",
    "is_weekend":          false,
    "is_festival_day":     false,
    "occasion":            "casual_grooming",
    "trust_signals": {
      "is_repeat_customer":          false,
      "prior_visits_with_partner":   0,
      "user_account_age_days":       312,
      "verified_phone":              true
    }
  }
}
Field Type Constraint Notes
intent string REQUIRED, equals "lifestyle.book_salon"
search_area.radius_km int REQUIRED, 1-25
scheduled_for_iso ISO_DATETIME REQUIRED
duration_minutes_estimated int REQUIRED, ≥0 sum of services
user_profile.gender enum REQUIRED, see §5 drives gender-specific service options
user_profile.age_band enum REQUIRED, see §5
user_profile.skin_kind enum REQUIRED, see §5 drives facial/wax service specificity
user_profile.hair_kind enum REQUIRED, see §5 drives haircut/color recommendations
user_profile.hair_length enum REQUIRED, STRICT bald | short | shoulder | long | extra_long
user_profile.current_color enum REQUIRED, see §5 drives color-correction options
services_wanted array REQUIRED, ≥1
services_wanted[].service_kind enum REQUIRED, see §5
services_wanted[].service_duration_estimate int REQUIRED, ≥0 partner may revise
services_wanted[].preferred_stylist_skill_min int REQUIRED, 1-5 skill level floor
preferences.salon_kind_acceptable array REQUIRED, ≥1 see §5
preferences.salon_kind_preferred enum REQUIRED, see §5
preferences.stylist_gender_preference enum REQUIRED, see §5
preferences.stylist_min_experience_years int REQUIRED, ≥0
preferences.stylist_certifications_required bool REQUIRED
preferences.stylist_kyc_required bool REQUIRED
preferences.products_kind_preferred array REQUIRED, may be empty see §5
preferences.ammonia_free_color_required bool REQUIRED
preferences.vegan_products_only bool REQUIRED
preferences.halal_products_required bool REQUIRED
preferences.budget_max_inr_total INR_INTEGER REQUIRED
preferences.lounge_amenity_required array REQUIRED, may be empty see §5
preferences.atmosphere_kind enum REQUIRED, see §5
preferences.max_wait_minutes_at_salon int REQUIRED, ≥0
preferences.online_payment_supported_required bool REQUIRED
context.is_weekend bool REQUIRED drives demand model
context.occasion enum REQUIRED, see §5

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


3. PROVIDER TOOLS

Tool 1: search_salon_options

PURPOSE:        return salon options with available slots for requested services
INPUT:          §2 request body
OUTPUT:         { results: SalonOption[], result_token, expires_at }
SLA:            p50 < 600ms, p95 < 1500ms

Tool 2: get_salon_detail

PURPOSE:        full salon profile with stylists + services + reviews
INPUT:          { salon_id, request_id }
OUTPUT:         SalonDetail (§4)
SLA:            p95 < 800ms

Tool 3: get_available_slots

PURPOSE:        live slots for given salon + services + date
INPUT:          { salon_id, service_kinds[], date_iso, stylist_id_optional, request_id }
OUTPUT:         { slots: SalonSlotOption[], expires_at }
SLA:            p95 < 500ms

Tool 4: book_salon_appointment

INPUT:          { salon_id, slot_id, services_wanted[], stylist_id, payment_token, request_id, idempotency_key, user_phone, special_requests }
OUTPUT:         { appointment_ref, status, slot_iso, stylist_assigned, total_inr, deposit_inr, cancellation_window_iso }
SLA:            p95 < 4000ms
IDEMPOTENCY:    REQUIRED on idempotency_key

Tool 5: modify_appointment

INPUT:          { appointment_ref, new_slot_iso, services_changes, stylist_change, request_id }
OUTPUT:         { revised_status, revised_total_inr, deposit_delta_inr }
SLA:            p95 < 2000ms

Tool 6: cancel_appointment

INPUT:          { appointment_ref, reason, request_id }
OUTPUT:         { status, refund_amount_inr, no_show_charge_inr, refund_processing_days }
SLA:            p95 < 2000ms

Tool 7: confirm_arrival

PURPOSE:        partner records check-in
INPUT:          { appointment_ref, observed_iso, request_id }
OUTPUT:         { acknowledged: true, wait_minutes_estimated }
SLA:            p95 < 800ms

Tool 8: confirm_service_complete

PURPOSE:        partner records service done + photos + bill
INPUT:          { appointment_ref, end_photos[], services_actually_performed, total_billed_inr, request_id }
OUTPUT:         { acknowledged: true, completion_iso }
SLA:            p95 < 1500ms

Tool 9: rate_salon

INPUT:          { appointment_ref, rating_5star, stylist_score, hygiene_score, ambience_score, comment, tip_inr, request_id }
OUTPUT:         { acknowledged: true }
SLA:            p95 < 800ms

All nine REQUIRED.


4. RESPONSE SHAPE

SalonOption

id:                               string, REQUIRED
option_token:                     string, REQUIRED
expires_at:                       ISO_DATETIME, REQUIRED

salon:
  id:                             string, REQUIRED
  merchant_id:                    string, REQUIRED
  name:                           string, REQUIRED
  brand:                          string, REQUIRED
  salon_kind:                     STRICT ENUM, REQUIRED       # see §5
  partner_account_age_days:       int, REQUIRED, ≥0
  total_appointments_completed_30day: int, REQUIRED, ≥0
  address:
    line_1:                       string, REQUIRED
    line_2:                       string, REQUIRED
    neighborhood:                 string, REQUIRED
    city:                         string, REQUIRED
    pincode:                      string, REQUIRED
    lat:                          float, REQUIRED
    lng:                          float, REQUIRED
    google_place_id:              string, REQUIRED
    landmark:                     string, REQUIRED
  distance_from_user_km:          float, REQUIRED
  ratings:
    overall_score:                float, REQUIRED, 0-5
    overall_count:                int, REQUIRED, ≥0
    recent_30day_score:           float, REQUIRED, 0-5
    cleanliness_score:            float, REQUIRED, 0-5
    stylist_skill_score:          float, REQUIRED, 0-5
    ambience_score:               float, REQUIRED, 0-5
    on_time_30day_pct:            float, REQUIRED, 0-1
  operating_hours:
    is_open_now:                  boolean, REQUIRED
    next_open_iso:                ISO_DATETIME, REQUIRED
    next_close_iso:               ISO_DATETIME, REQUIRED
    hours_of_operation:           array, REQUIRED, 7 entries
      - day_of_week:              STRICT ENUM, REQUIRED
        open_iso_time:            string, REQUIRED
        close_iso_time:           string, REQUIRED
        closed_today:             boolean, REQUIRED
  amenities:
    air_conditioning:             boolean, REQUIRED
    music_system:                 boolean, REQUIRED
    private_rooms:                boolean, REQUIRED
    wifi_available:               boolean, REQUIRED
    valet_parking_available:      boolean, REQUIRED
    self_park_lot:                boolean, REQUIRED
    refreshments_complimentary:   boolean, REQUIRED
    children_section:             boolean, REQUIRED
    elderly_friendly:             boolean, REQUIRED
    wheelchair_accessible:        boolean, REQUIRED
    female_friendly_certified:    boolean, REQUIRED
    cctv_in_premises:             boolean, REQUIRED
  trust:
    fssai_grade_for_food:         STRICT ENUM, REQUIRED        # if cafe inside
    municipal_license_number:     string, REQUIRED
    municipal_license_valid_until: ISO_DATE, REQUIRED
    hygiene_audit_iso:            ISO_DATETIME, REQUIRED
    hygiene_audit_score:          int, REQUIRED, 0-100
    sterilization_certified:      boolean, REQUIRED
    bbb_safety_certified:         boolean, REQUIRED            # blade/blade-handling code
    employee_kyc_pct:             int, REQUIRED, 0-100
    tomo_field_team_audited:      boolean, REQUIRED
  photos:
    thumbnail_url:                URL, REQUIRED
    hero_url:                     URL, REQUIRED
    interior_photos_url:          URL, REQUIRED
    work_photos_url:              URL, REQUIRED                # past haircuts/colors
    ai_generated_photos:          boolean, REQUIRED            # MUST be false

services_offered:                 array<ServiceCatalogItem>, REQUIRED, ≥3
total_services_count:             int, REQUIRED, ≥1

available_slots:                  array<SalonSlotOption>, REQUIRED, ≥1

stylists:                         array<StylistProfile>, REQUIRED, ≥1

products_used:
  preferred_brands:               array<STRICT ENUM>, REQUIRED, ≥1
  ammonia_free_color_available:   boolean, REQUIRED
  vegan_products_only:            boolean, REQUIRED
  halal_certified_products:       boolean, REQUIRED
  cruelty_free:                   boolean, REQUIRED
  imported_brand_premium:         boolean, REQUIRED

freshness:
  data_last_synced_iso:           ISO_DATETIME, REQUIRED
  catalog_last_updated_iso:       ISO_DATETIME, REQUIRED
  prices_last_updated_iso:        ISO_DATETIME, REQUIRED

_provider:
  name:                           string, REQUIRED
  tomo_partner_id:                string, REQUIRED
  partner_tier:                   STRICT ENUM, REQUIRED
  deep_link:                      URL, REQUIRED
  customer_support_phone:         string, REQUIRED
  customer_support_email:         string, REQUIRED
  customer_support_24x7:          boolean, REQUIRED
  in_app_chat_supported:          boolean, REQUIRED
  partner_salon_volume_30d:       int, REQUIRED, ≥0
  partner_salon_completion_rate_30d: float, REQUIRED, 0-1

ServiceCatalogItem

service_id:                       string, REQUIRED
service_kind:                     STRICT ENUM, REQUIRED         # see §5
service_name:                     string, REQUIRED              # "Balayage Color"
description:                      string, REQUIRED
gender_for:                       STRICT ENUM, REQUIRED         # see §5
duration_minutes:                 int, REQUIRED, ≥1
price_inr:                        INR_INTEGER, REQUIRED
mrp_inr:                          INR_INTEGER, REQUIRED
discount_pct:                     float, REQUIRED, 0-1          # 0 if no discount
includes:                         array<string>, REQUIRED, may be empty   # e.g., ["wash", "blow dry"]
products_used:                    array<string>, REQUIRED, ≥1
photo_url:                        URL, REQUIRED                 # of style example
photo_ai_generated:               boolean, REQUIRED              # MUST be false
skill_level_min:                  int, REQUIRED, 1-5             # min stylist skill
recovery_minutes:                 int, REQUIRED, ≥0              # post-service like setting time
patch_test_required:              boolean, REQUIRED              # for color/perm
allergen_disclosure:              array<STRICT ENUM>, REQUIRED, may be empty

StylistProfile

stylist_id:                       string, REQUIRED
display_name:                     string, REQUIRED
photo_url:                        URL, REQUIRED
photo_ai_generated:               boolean, REQUIRED              # MUST be false
gender:                           STRICT ENUM, REQUIRED
age_band:                         STRICT ENUM, REQUIRED
experience_years:                 int, REQUIRED, ≥0
skill_level:                      int, REQUIRED, 1-5             # partner-internal
specializations:                  array<STRICT ENUM>, REQUIRED, ≥1   # see §5
certifications:                   array<STRICT ENUM>, REQUIRED, may be empty
certifying_authorities:           array<STRICT ENUM>, REQUIRED, may be empty
languages_spoken:                 array<RFC_3066_LOCALE>, REQUIRED, ≥1
ratings:
  rating_avg:                     float, REQUIRED, 0-5
  rating_count:                   int, REQUIRED, ≥0
  appointments_completed_total:   int, REQUIRED, ≥0
  appointments_completed_30day:   int, REQUIRED, ≥0
kyc:
  aadhaar_verified:               boolean, REQUIRED
  pan_verified:                   boolean, REQUIRED
  background_check_passed:        boolean, REQUIRED
  background_check_iso:           ISO_DATETIME, REQUIRED
employed_kind:                    STRICT ENUM, REQUIRED         # see §5
sample_work_gallery_url:          URL, REQUIRED                 # JSON manifest of past work photos

SalonSlotOption

slot_id:                          string, REQUIRED
slot_iso:                         ISO_DATETIME, REQUIRED
duration_minutes:                 int, REQUIRED
stylist_id_assigned:              string, REQUIRED
stylist_display_name:             string, REQUIRED
chair_or_room_id:                 string, REQUIRED              # internal
arrival_window_minutes:           int, REQUIRED                  # how late before slot released
deposit_inr:                      INR_INTEGER, REQUIRED          # 0 if no deposit
cancellation_until_iso:           ISO_DATETIME, REQUIRED
no_show_charge_inr:               INR_INTEGER, REQUIRED
slot_kind:                        STRICT ENUM, REQUIRED          # see §5
freshness_iso:                    ISO_DATETIME, REQUIRED

SalonDetail (returned by get_salon_detail)

Superset of salon block plus full service catalog (≥10 services), all stylists, expanded reviews, complete past-work gallery.

Forbidden fields

paid_placement_score | sponsored_rank | promotion_priority |
fake_recent_appointment_text | auto_inflate_completion_count |
ai_generated_photos (must equal false) | hidden_service_fee |
fake_certifications | undisclosed_blade_reuse |
fake_partner_completion_rate_30d

5. CONTROLLED VOCABULARIES

salon_kind

unisex | ladies | mens | bridal_specialist | luxury_spa_salon |
mens_grooming_lounge | celebrity_brand | franchise_chain | premium_independent |
budget_chain | barbershop | hair_studio | nails_specialty

user_profile.gender

female | male | non_binary | prefer_not_to_say

user_profile.age_band and StylistProfile.age_band

18-25 | 26-35 | 36-45 | 46-55 | 56+

user_profile.skin_kind

oily | dry | combination | normal | sensitive | acne_prone | aged

user_profile.hair_kind

straight | wavy | curly | coily | thin | thick | damaged | greying

user_profile.current_color

natural_black | natural_brown | dyed_black | dyed_brown | dyed_red |
dyed_blonde | dyed_burgundy | highlighted | balayage | ombre |
greying_partial | greying_full | other

services_wanted[].service_kind and ServiceCatalogItem.service_kind

haircut_men | haircut_women | haircut_kids | haircut_styling |
shave_clean | shave_beard_trim | beard_styling | mustache_trim |
hair_color_global | hair_color_root | hair_color_balayage |
hair_color_highlights | hair_color_ombre | hair_color_correction |
hair_smoothing | hair_keratin | hair_botox | hair_perm |
hair_spa_oil | hair_spa_deep_conditioning |
facial_basic | facial_premium | facial_hydra | facial_oxygen |
facial_anti_aging | facial_brightening | clean_up_basic |
manicure_basic | manicure_french | manicure_gel | manicure_acrylic |
pedicure_basic | pedicure_premium | pedicure_paraffin |
nail_art | nail_extensions |
waxing_full_arms | waxing_full_legs | waxing_underarm |
waxing_brazilian | waxing_facial | threading_eyebrows | threading_face |
massage_head | massage_back | massage_foot |
makeup_party | makeup_bridal | makeup_engagement | makeup_natural |
makeup_smokey | hair_styling_for_event |
bridal_package | groom_package | engagement_package | pre_wedding_glow_package

ServiceCatalogItem.gender_for

female | male | unisex | child

services_wanted[].preferred_stylist_skill_min (1-5 scale)

  • 1: Trainee
  • 2: Junior
  • 3: Standard
  • 4: Senior
  • 5: Master / Celebrity

preferences.salon_kind_acceptable[]

Same enum as salon_kind.

preferences.stylist_gender_preference

any | female | male

preferences.products_kind_preferred[] and salon.products_used.preferred_brands[]

loreal | schwarzkopf | matrix | wella | redken | kerastase |
ohcc | nashi | indola | revlon | streax | godrej_expert |
patanjali | lakme | maybelline | nykaa_in_house | partner_house_brand |
imported_premium | other

preferences.lounge_amenity_required[]

air_conditioning | music | private_room | wifi | refreshments |
children_section | quiet_zone

preferences.atmosphere_kind

calm | lively | luxe | minimal | trendy | community

salon.trust.fssai_grade_for_food

A | B | C | unrated | not_applicable

salon.amenities and preferences.lounge_amenity_required

(Same enum bridges across both — see preferences list)

StylistProfile.gender

female | male | non_binary | prefer_not_to_say

StylistProfile.specializations[]

haircuts | hair_color | balayage_specialist | keratin_specialist |
facials | makeup_party | makeup_bridal | nail_art | nail_extensions |
waxing | threading | massages | mens_grooming | beard_styling |
hair_extensions | bridal_packages | celebrity_styling

StylistProfile.certifications[]

loreal_certified | schwarzkopf_certified | matrix_certified |
toni_guy_certified | sassoon_certified | iihm_certified |
state_council_certified | foreign_school_certified

StylistProfile.certifying_authorities[]

loreal | schwarzkopf | matrix | toni_and_guy | vidal_sassoon |
iihm | indian_state_council | international_school | partner_internal

StylistProfile.employed_kind

full_time_employee | part_time_employee | contract | freelance |
guest_artist

SalonSlotOption.slot_kind

standard | premium_slot | early_morning | late_evening |
weekend_premium | luxury_slot | walk_in_compatible

ServiceCatalogItem.allergen_disclosure[]

ammonia | hydrogen_peroxide | parabens | sulphates | silicones |
synthetic_fragrance | nut_extracts | dairy_extracts | egg_extracts |
formaldehyde | latex | none

cancel_appointment.reason

user_changed_plans | wrong_time | wrong_service | found_alternative |
medical_emergency | weather | partner_canceled | other

context.occasion

casual_grooming | event | bridal_prep | groom_prep |
date_night | party | religious_event | photoshoot | other

merchant_id resolution order

1. Google Place ID
2. Municipal license number
3. partner_id + ":" + salon_id

6. TTBS DIMENSIONS

Per-domain weights (locked; lifestyle-salon)

lifestyle (book_salon): { time: 0.20, taste: 0.40, budget: 0.30, safety: 0.10 }

TIME

SIGNALS USED:
  - slot_iso match request                           HARD FILTER (within ±30min)
  - distance_from_user_km                            weight 0.30
  - operating_hours.is_open_now                      HARD FILTER
  - SlotOption.arrival_window_minutes (longer=better) weight 0.20
  - salon.ratings.on_time_30day_pct                  weight 0.30
  - max_wait_minutes_at_salon (lower=better)         weight 0.20

TASTE

SIGNALS USED:
  - salon.ratings.stylist_skill_score                weight 0.30
  - salon.ratings.recent_30day_score                 weight 0.20
  - stylist.skill_level vs preferred_stylist_skill_min weight 0.20
  - stylist.specializations match services_wanted    weight 0.20
  - amenity match w/ preferences                     weight 0.10
  - products_used overlap w/ products_kind_preferred  weight 0.10

HARD FILTERS:
  - stylist_gender_preference mismatch
  - salon_kind not in salon_kind_acceptable
  - ammonia_free_color_required AND ammonia_free_color_available=false → drop hair-color services
  - vegan_products_only AND vegan_products_only=false → drop
  - halal_products_required AND halal_certified_products=false → drop

BUDGET

SIGNALS USED:
  - service total_inr vs band:
      ok    → budget_chain / barbershop
      good  → premium_independent / mens_grooming_lounge
      great → luxury_spa_salon / celebrity_brand
  - SalonSlotOption.deposit_inr (lower=better)       weight 0.20
  - SalonSlotOption.no_show_charge_inr               weight 0.10
  - service.discount_pct (higher=better)             weight 0.20
  - cancellation tier permissiveness                 weight 0.15
  - online_payment_supported (if required)           HARD FILTER

HARD FILTERS:
  - sum service prices > preferences.budget_max_inr_total → drop

SAFETY

SIGNALS USED:
  - salon.trust.hygiene_audit_score (>80=good)       weight 0.30
  - salon.trust.sterilization_certified              HARD FILTER
  - salon.trust.bbb_safety_certified (blade reuse)   HARD FILTER
  - salon.trust.employee_kyc_pct (≥80)               weight 0.20
  - stylist.kyc.background_check_passed              HARD FILTER
  - stylist.kyc.aadhaar_verified                     HARD FILTER (if stylist_kyc_required)
  - salon.amenities.female_friendly_certified
      (for ladies salon, female user)                weight 0.20
  - salon.amenities.cctv_in_premises                 weight 0.10
  - allergen disclosure honest for color services    weight 0.20

HARD FILTERS:
  - stylist_kyc_required AND stylist KYC fields=false → drop
  - certain services (color/keratin) with allergens user reports allergic → drop

Hidden ranking factor

information_completeness_score weight 0.10.


7. COMPLETION CONTRACT

POST /api/v1/cpc/mcp_provider/{tomo_partner_id}
{
  "intent":            "lifestyle.book_salon",
  "intent_version":    "v1.0.0",
  "external_id":       "SLN-XYZ",
  "amount_inr":         3850,
  "closed_at":         "2026-05-12T13:14:00+05:30",
  "request_id":        "req_sln_8h2k_...",
  "status":            "completed",
  "appointment_ref":   "SLN-XYZ",
  "merchant_id":       "ChIJxxxxx",
  "salon_id":          "salon_8423",
  "stylist_id":        "stylist_xyz",
  "checked_in_at":     "2026-05-12T11:02:00+05:30",
  "service_started_at": "2026-05-12T11:08:00+05:30",
  "service_completed_at": "2026-05-12T13:00:00+05:30",
  "promised_slot_iso": "2026-05-12T11:00:00+05:30",
  "wait_minutes_actual": 8,
  "services_performed": ["hair_color_balayage", "haircut_styling"],
  "services_skipped":   [],
  "products_used_inr":  450,
  "currency":          "INR",
  "fare_breakdown_total_inr": 3850,
  "rider_tip_inr":       300,
  "ratings_pending":     true,
  "notes":              ""
}

Status enum: completed | cancelled_by_user | cancelled_by_salon | no_show | failed | partial_completion_user_left_early


8. WIDGET

WIDGET TYPE:        salon_options
SOURCE:             src/widgets/types.ts
TYPE NAME:          SalonOptionsPayload
RENDERED IN:        components/widgets/SalonOptionsWidget.tsx

Default: 3 stacked rows showing salon name, salon_kind, services-fitting-pill, slot-time pill, rating + cleanliness badge. Tap row → salon detail with full service catalog + stylist profiles + work gallery + reviews → "Book". Then SalonAppointmentCard with QR code + arrival window + directions.


9. CACHING POLICY

Call TTL Rationale
search_salon_options 60s slot availability shifts
get_salon_detail 5min catalog + stylists static-ish
get_available_slots 30s live
book_salon_appointment 0s
modify_appointment 0s
cancel_appointment 0s
confirm_arrival 0s
confirm_service_complete 0s
Failure responses 0s

10. ERROR CODES

Code HTTP Meaning TOMO behavior
NO_SLOTS_AVAILABLE 503 no slots match suggest alternates
STYLIST_UNAVAILABLE 503 preferred stylist unavailable fall back
SALON_CLOSED 409 salon not open at slot drop, refresh
OUT_OF_SERVICE_AREA 400 outside coverage surface
OPTION_EXPIRED 410 option_token invalid re-quote
SLOT_JUST_TAKEN 409 another user booked it re-quote
SERVICE_DURATION_INFEASIBLE 400 combo doesn't fit slot suggest split
DEPOSIT_REQUIRED 402 deposit not paid surface
APPOINTMENT_NOT_FOUND 404 appointment_ref doesn't exist surface
ALREADY_CANCELLED 409 duplicate cancel idempotent
STYLIST_KYC_INVALID 403 stylist KYC expired partner reassigns

11. SANDBOX → PRODUCTION CHECKLIST

[ ] All §2 inputs validated, request_id echoed
[ ] search_salon_options returns ≥10 options for "haircut+color Hyderabad Saturday"
[ ] All §4 fields populated with REAL data
[ ] municipal_license + hygiene audit current
[ ] photo_ai_generated == false everywhere (incl. stylists)
[ ] Service catalog complete with prices, duration, products
[ ] book_salon_appointment returns valid appointment_ref + stylist within SLA
[ ] modify and cancel respect cancellation policies
[ ] confirm_arrival fires on user check-in
[ ] confirm_service_complete with bill upload
[ ] CPC webhook within 60s of completion
[ ] HMAC verification passes
[ ] No forbidden fields anywhere
[ ] No paid placement / sponsored signals
[ ] customer_support_24x7 verified by TOMO call
[ ] Stylist KYC artifacts uploaded
[ ] Sterilization protocol verifiable
[ ] Female-friendly certification verified (if claimed)
[ ] Wheelchair accessibility tested in person

12. ANTI-FABRICATION RULES

RULE 1 — No paid placement signals.

RULE 2 — Ratings unrounded floats from verified appointments only.

RULE 3 — No fake completion count inflation.

RULE 4 — No AI-generated photos (salon, stylist, work gallery).

RULE 5 — Sterilization is real.
  sterilization_certified=true means actual UV/autoclave protocol with
  observable equipment. Reusing blades on multiple customers without
  sterilization = severe health/safety breach + criminal exposure.

RULE 6 — Blade safety code (BBB) honest.
  bbb_safety_certified means single-use blade per customer. False claim
  = severe breach.

RULE 7 — Stylist credentials real and verifiable.
  certifications must have certificate ID. Self-attesting "Schwarzkopf
  certified" without cert = breach.

RULE 8 — No commission-based stylist ranking.
  Stylist surfaced first must be by skill match + rating, not partner
  commission.

RULE 9 — Allergen disclosure complete.
  Color/keratin services that contain ammonia must declare it. Hidden
  PPD/aminobenzene claims = consumer-protection breach.

RULE 10 — Patch test honored.
  patch_test_required=true for color must actually be performed 24-48h
  before service. Skipping = breach + safety hazard.

RULE 11 — No silent stylist swap.
  If booked stylist unavailable, replacement requires user accept.

RULE 12 — Service duration honest.
  duration_minutes claimed must match actual. Padding to extract higher
  prices = breach.

RULE 13 — Products disclosed honestly.
  products_used must reflect what's actually used in the chair. Generic
  hidden brand swapped at the chair = breach.

RULE 14 — No commission-based response shaping.

RULE 15 — Female-friendly certification real.
  Real partner program with documented protocol. False claim = breach.

RULE 16 — Hygiene audit current.
  hygiene_audit_iso must be within last 90 days. Stale audits cited as
  current = breach.

VERSION HISTORY

v1.0.0 — 2026-05-10 — Initial spec (sets slot-based service-provider shape for lifestyle domain)