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)