travel.book_adventure_activity — Full Intent Specification
INTENT NAMESPACE: travel
INTENT NAME: book_adventure_activity
FULL ID: travel.book_adventure_activity
VERSION: v1.0.0
STATUS: draft
TTBS WEIGHTS: time 0.15 · taste 0.25 · budget 0.20 · safety 0.40
LAST UPDATED: 2026-05-14
User books a single adventure or experiential activity — trekking, paragliding, river rafting, scuba diving, bungee jumping, hot-air ballooning, skydiving, jungle safari, rock climbing, kayaking. Single-day or multi-day single-activity engagements. Distinct from travel.book_tour_guide (information-led walking tours) and travel.book_package (multi-component bundles). Safety dominates — these are physical, weather-dependent, certification-gated experiences. Partner exemplars: Thrillophilia, Padi Travel, IndiaHikes, Trek The Himalayas, Bhushan Adventures, BookMyAdventures, Adventure Nation, MakeMyTrip Activities, local-operator marketplaces.
1. NATURAL LANGUAGE COVERAGE
Classifies IN
- "paragliding in Bir Billing"
- "river rafting Rishikesh"
- "scuba diving Andaman"
- "trek to Hampta Pass"
- "bungee Goa"
- "skydive Mysuru"
- "hot air balloon Jaipur"
- "kayaking in Goa"
- "rock climbing Hampi"
- "jungle safari Bandhavgarh"
Classifies OUT — borderline NO
- "tour guide for Hampi" →
travel.book_tour_guide - "Hampi trip with hotel" →
travel.book_package - "kayak rental only" — out of scope v1 (equipment rental marketplace)
- "adventure park bangalore" — generic park entry is theme-park ticketing, not certified-adventure
- "gym workout" →
lifestyle.book_gym_session
MULTI-INTENT TRIGGERS
- "Rafting + hotel Rishikesh" →
travel.book_adventure_activity+travel.book_hotel - "Trek + flight to Manali" →
travel.book_adventure_activity+travel.book_flight - "Scuba + travel insurance" →
travel.book_adventure_activity+finance.buy_travel_insurance
2. INPUT — TOMO → PROVIDER
{
"intent": "travel.book_adventure_activity",
"intent_version": "v1.0.0",
"request_id": "req_adv_2h4k_2026-05-14T10:00:00+05:30",
"user_session_id": "anon_user_token_or_uid",
"activity_request": {
"activity_kind": "paragliding",
"activity_grade": "beginner",
"location_city": "Bir",
"location_state": "Himachal Pradesh",
"country_code": "IN",
"activity_date": "2026-10-12",
"flexible_days_window": 2,
"preferred_time_window": {
"start": "2026-10-12T09:00:00+05:30",
"end": "2026-10-12T15:00:00+05:30"
},
"duration_hours": 1.5,
"party": {
"adult_count": 2,
"children_with_age": [],
"seniors_count": 0
},
"participant_health_declarations": [
{ "age": 32, "weight_kg": 78, "height_cm": 178, "pre_existing_conditions": [], "pregnant": false }
],
"experience_level": "first_time",
"training_included_required": true,
"tandem_or_solo": "tandem",
"instructor_language": "en",
"instructor_language_alternates": ["hi"],
"transport_included_required": false,
"media_capture_included_required": true,
"budget_band": "good",
"budget_max_inr_total": 8000
},
"context": {
"user_locale": "en-IN",
"user_currency_pref": "INR",
"trust_signals": {
"is_repeat_customer": false,
"user_account_age_days": 312
}
}
}
| Field | Type | Constraint | Notes |
|---|---|---|---|
intent |
string | REQUIRED, equals "travel.book_adventure_activity" |
|
activity_request.activity_kind |
enum | REQUIRED, STRICT §6 | paragliding / rafting / scuba / trek / bungee / skydive / balloon / safari / climb / kayak / zipline |
activity_request.activity_grade |
enum | REQUIRED, STRICT §6 | beginner / intermediate / advanced |
activity_request.activity_date |
ISO_DATE | REQUIRED | Up to 180 days advance |
activity_request.duration_hours |
float | REQUIRED, 0.5-72 | Multi-day for treks |
activity_request.party.adult_count |
int | REQUIRED, ≥1, ≤20 | |
activity_request.participant_health_declarations |
array | REQUIRED, ≥party.adult_count | Per-participant health |
activity_request.participant_health_declarations[].age |
int | REQUIRED | Age limits per activity |
activity_request.participant_health_declarations[].weight_kg |
int | REQUIRED | Critical for paragliding / bungee / zipline |
activity_request.participant_health_declarations[].height_cm |
int | REQUIRED | |
activity_request.participant_health_declarations[].pre_existing_conditions |
array |
REQUIRED, STRICT §6, may be empty | Triggers safety filter |
activity_request.participant_health_declarations[].pregnant |
bool | REQUIRED | Hard filter for high-impact activities |
activity_request.experience_level |
enum | REQUIRED, STRICT §6 | first_time / occasional / experienced / certified |
activity_request.training_included_required |
bool | REQUIRED | Hard filter on operators who skip briefing |
activity_request.tandem_or_solo |
enum | REQUIRED, STRICT §6 | tandem / solo / either |
activity_request.instructor_language |
enum | REQUIRED, STRICT §6 | |
activity_request.media_capture_included_required |
bool | REQUIRED | Drives bundle vs add-on |
Anti-fabrication preamble: no paid placement. Operator certifications (PG Association of India, ITRA for trekking, PADI / SSI for scuba, MoT Adventure Tour Operator AAT recognition) MUST be verifiable. Equipment-age + last-service-date MUST be disclosed. Weather-dependent operators MUST publish their cancellation criteria. TOMO never holds the rail.
3. PROVIDER TOOLS
Tool 1: search_activities
PURPOSE: return operators offering activity in location on date
INPUT: §2 request body
OUTPUT: { activities: ActivityOption[], result_token, expires_at }
SLA: p50 < 800ms, p95 < 2500ms, p99 < 4500ms
RATE LIMIT: ≤ 1/sec per (user_session_id, partner)
RESULT SET: up to 15 operators
Tool 2: get_activity_detail
PURPOSE: full briefing + equipment + instructor + safety record
INPUT: { activity_id, request_id }
OUTPUT: ActivityDetail (§5)
SLA: p95 < 1500ms
Tool 3: hold_activity_slot
PURPOSE: soft-lock for 15 minutes
INPUT: { activity_id, party, health_declarations, request_id }
OUTPUT: { hold_id, hold_expires_at, locked_price_inr, indemnity_url }
SLA: p95 < 1200ms
Tool 4: confirm_activity
PURPOSE: confirm + indemnity-signature capture + voucher
INPUT: { hold_id, indemnity_e_signed, payment_method_id, request_id }
OUTPUT: ConfirmedActivity (§5)
SLA: p95 < 3500ms
IDEMPOTENCY: request_id
Tool 5: cancel_activity
PURPOSE: cancel + refund (auto-full-refund on weather cancel)
INPUT: { booking_id, reason_code, request_id }
OUTPUT: CancellationResult (§5)
SLA: p95 < 2500ms
4. RESPONSE SHAPE
ActivityOption
ActivityOption:
activity_id: { type: string, constraint: REQUIRED, opaque }
activity_kind: { type: enum, constraint: REQUIRED, STRICT §6 }
activity_grade: { type: enum, constraint: REQUIRED, STRICT §6 }
operator:
operator_id: { type: string, constraint: REQUIRED }
name: { type: string, constraint: REQUIRED }
aat_recognition: { type: boolean, constraint: REQUIRED, semantics: "Ministry of Tourism Adventure Tour Operator recognition" }
certifying_bodies: { type: array<enum>, constraint: REQUIRED, STRICT §6, ≥1, semantics: "PADI / SSI / IFMGA / ITRA / PGAI etc." }
operator_registration_number: { type: string, constraint: REQUIRED }
years_operating: { type: int, constraint: REQUIRED, ≥0 }
incident_count_past_12_months: { type: int, constraint: REQUIRED, ≥0, semantics: "Reported incidents to MoT or insurer" }
rescue_team_onsite: { type: boolean, constraint: REQUIRED }
medical_kit_certified: { type: boolean, constraint: REQUIRED }
insurance_covers_participants: { type: boolean, constraint: REQUIRED }
insurance_sum_assured_inr_per_participant: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
instructor:
name: { type: string, constraint: REQUIRED }
certification_kind: { type: enum, constraint: REQUIRED, STRICT §6 }
certification_id: { type: string, constraint: REQUIRED }
certification_valid_until: { type: string, constraint: REQUIRED, ISO_DATE }
years_instructing: { type: int, constraint: REQUIRED, ≥0 }
languages_spoken: { type: array<enum>, constraint: REQUIRED, STRICT §6, ≥1 }
schedule:
starts_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
ends_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
briefing_starts_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
pricing:
base_per_participant_inr: { type: int, constraint: REQUIRED, INR_INTEGER }
tandem_premium_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
media_capture_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
transport_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
gst_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
total_for_party_inr: { type: int, constraint: REQUIRED, INR_INTEGER }
equipment:
list: { type: array<string>, constraint: REQUIRED, ≥1 }
last_service_dates: { type: array<string>, constraint: REQUIRED, ISO_DATE, ≥1 }
manufacturer_certified: { type: boolean, constraint: REQUIRED }
eligibility:
age_min: { type: int, constraint: REQUIRED }
age_max: { type: int, constraint: REQUIRED }
weight_min_kg: { type: int, constraint: REQUIRED, ≥0 }
weight_max_kg: { type: int, constraint: REQUIRED, ≥0 }
height_min_cm: { type: int, constraint: REQUIRED, ≥0 }
height_max_cm: { type: int, constraint: REQUIRED, ≥0 }
fitness_requirement: { type: enum, constraint: REQUIRED, STRICT §6, values: [low, moderate, high, expert] }
contraindications: { type: array<enum>, constraint: REQUIRED, STRICT §6, may be empty }
weather_policy:
auto_cancel_conditions: { type: array<enum>, constraint: REQUIRED, STRICT §6, may be empty }
auto_refund_on_weather: { type: boolean, constraint: REQUIRED }
cancellation_policy:
type: array<object>
constraint: REQUIRED, may NOT be empty
shape:
hours_before_start_min: { type: int, constraint: REQUIRED }
hours_before_start_max: { type: int, constraint: REQUIRED }
refund_pct: { type: int, constraint: REQUIRED, 0-100 }
rating:
average_score: { type: float, constraint: REQUIRED, 0.0-5.0 }
review_count: { type: int, constraint: REQUIRED, ≥0 }
partner_reference:
source: { type: string, constraint: REQUIRED }
deeplink: { type: string, constraint: REQUIRED, HTTPS URL }
ActivityDetail
ActivityDetail:
activity_id: { type: string, constraint: REQUIRED }
full_briefing_text: { type: string, constraint: REQUIRED }
pre_activity_requirements: { type: array<string>, constraint: REQUIRED, ≥1 }
what_to_carry_text: { type: array<string>, constraint: REQUIRED, ≥1 }
what_is_provided_text: { type: array<string>, constraint: REQUIRED, ≥1 }
emergency_protocol_text: { type: string, constraint: REQUIRED }
nearest_hospital_name: { type: string, constraint: REQUIRED }
nearest_hospital_distance_km: { type: float, constraint: REQUIRED, ≥0 }
evacuation_plan_text: { type: string, constraint: REQUIRED }
indemnity_form_url: { type: string, constraint: REQUIRED, HTTPS URL }
ConfirmedActivity
ConfirmedActivity:
booking_id: { type: string, constraint: REQUIRED, immutable }
activity_id: { type: string, constraint: REQUIRED }
operator_id: { type: string, constraint: REQUIRED }
starts_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
briefing_starts_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
meeting_point:
address: { type: string, constraint: REQUIRED }
lat: { type: float, constraint: REQUIRED }
lng: { type: float, constraint: REQUIRED }
landmark: { type: string, constraint: REQUIRED }
participants_confirmed:
type: array<object>
constraint: REQUIRED, ≥1
shape:
participant_name: { type: string, constraint: REQUIRED }
age: { type: int, constraint: REQUIRED }
health_declared: { type: boolean, constraint: REQUIRED }
indemnity_signed: { type: boolean, constraint: REQUIRED }
indemnity_signed_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
total_paid_inr: { type: int, constraint: REQUIRED, INR_INTEGER }
voucher_url: { type: string, constraint: REQUIRED, HTTPS URL }
operator_phone_at_t_minus_4h: { type: string, constraint: REQUIRED, E.164, semantics: "Empty until 4h before" }
emergency_24_7_phone: { type: string, constraint: REQUIRED, E.164 }
weather_check_at: { type: string, constraint: REQUIRED, ISO_DATETIME, semantics: "Operator posts go/no-go decision at this time" }
cancellation_window:
full_refund_until: { type: string, constraint: REQUIRED, ISO_DATETIME }
last_cancellable_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
CancellationResult
CancellationResult:
booking_id: { type: string, constraint: REQUIRED }
cancelled_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
cancellation_reason: { type: enum, constraint: REQUIRED, STRICT §6 }
refund_amount_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
refund_method: { type: enum, constraint: REQUIRED, STRICT §6, values: [original_payment, bank_transfer] }
refund_eta_days: { type: int, constraint: REQUIRED, 0-10 }
weather_initiated: { type: boolean, constraint: REQUIRED }
FORBIDDEN FIELDS
paid_placement_score,ad_bid,sponsored_rank,featured_operatoreditor_pick,tomo_recommended,top_choiceunderstated_incident_count,concealed_incidentexpired_certification_idunservied_equipment— last_service_date older than 12 months without disclosureai_generated_activity_photo,staged_safety_photocommission_padded_per_participant_inrsuccess_guarantee_text("100% safe!" — adventure activities have residual risk; absolute guarantee forbidden)participant_real_namesin reviews
5. CONTROLLED VOCABULARIES
activity_request.activity_kind / ActivityOption.activity_kind:
values:
paragliding, river_rafting, scuba_diving, trekking_day, trekking_multi_day,
mountaineering, bungee_jumping, hot_air_balloon, skydiving,
jungle_safari_wildlife, jungle_safari_birding, rock_climbing,
rappelling, kayaking, white_water_kayaking, surfing, kitesurfing,
zipline, atv_quad, snorkeling, caving_spelunking, ice_skating_natural,
snowboarding, skiing, dog_sledding, microlight_flight, helicopter_joyride
activity_grade:
values: [beginner, intermediate, advanced]
operator.certifying_bodies / instructor.certification_kind:
values:
PADI: "PADI scuba (recreational)"
SSI: "Scuba Schools International"
IFMGA: "International Federation of Mountain Guides Associations"
ITRA: "Indian Travelers' & Trekkers' Association"
PGAI: "Paragliding Association of India"
IMF: "Indian Mountaineering Foundation"
AAT: "Approved Adventure Tour Operator (MoT)"
RBI_safety: "Rafting Body of India safety certification"
SAA: "Skydiving Association of America (USPA)"
INSA: "Indian National Skydiving Association"
BBA: "Balloon Federation operating license"
state_safari: "State forest department safari permit"
USCG: "US Coast Guard kayak instructor"
other_certified: "Other recognized international body"
eligibility.fitness_requirement:
values: [low, moderate, high, expert]
eligibility.contraindications:
values:
cardiac_condition, hypertension_uncontrolled, asthma_moderate,
asthma_severe, diabetes_brittle, epilepsy_uncontrolled, pregnancy,
recent_surgery_6_months, recent_fracture_6_months, ear_problems,
sinus_problems, vertigo, severe_obesity, severe_anxiety_disorder,
blood_thinners
weather_policy.auto_cancel_conditions:
values:
high_wind_above_30kmph, lightning_within_10km, fog_visibility_under_500m,
rain_above_moderate, water_current_above_safe_threshold,
sea_swell_above_2m, snowstorm, monsoon_active_period,
avalanche_advisory_active
participant_health_declarations[].pre_existing_conditions:
values: (same as eligibility.contraindications + 'none' + 'other_disclosed')
experience_level:
values: [first_time, occasional, experienced, certified]
tandem_or_solo:
values: [tandem, solo, either]
instructor.languages_spoken / activity_request.instructor_language:
values: [en, hi, te, ta, kn, ml, mr, bn, gu, pa, fr, es, de, ja, zh]
CancellationResult.cancellation_reason:
values:
user_changed_mind, weather_unsafe, equipment_failure,
operator_overbooked, participant_health_disclosed_post_book,
minimum_party_not_met, government_advisory, natural_disaster
6. TTBS DIMENSIONS
TIME (weight = 0.15):
signals_used:
- earliest available slot in preferred_time_window
- briefing_starts_at fits user's arrival window
weighting:
slot_fit: 0.70
briefing_overlap: 0.30
user_band_handling:
fast: prefer first-available
balanced: standard
flexible: 3-day window OK
TASTE (weight = 0.25):
signals_used:
- activity_grade match with experience_level
- tandem_or_solo match
- media_capture_included_required match
- instructor_language match
- DNA repeat operator
weighting:
grade_experience_fit: 0.30
tandem_solo_match: 0.20
media_match: 0.20
instructor_language: 0.15
dna_repeat: 0.15
BUDGET (weight = 0.20):
signals_used:
- pricing.total_for_party_inr
- media_capture_inr (add-on cost)
weighting:
total: 0.85
addons: 0.15
user_band_handling:
ok: cheapest passing safety + grade-fitness floors
good: balanced
great: premium operator with rescue team + best gear
SAFETY (weight = 0.40):
signals_used:
- operator.aat_recognition (boost when TRUE)
- operator.certifying_bodies covers activity_kind (HARD filter for scuba/paragliding/skydive)
- operator.years_operating ≥3 floor
- operator.incident_count_past_12_months / participants_per_year (lower preferred)
- operator.rescue_team_onsite (HARD for water / high-altitude / high-impact)
- operator.medical_kit_certified (HARD)
- operator.insurance_covers_participants (HARD)
- instructor.certification valid + active
- instructor.years_instructing ≥2 floor for advanced grade
- equipment.last_service_dates within 12 months
- eligibility checks pass for ALL participants
- weather_policy.auto_refund_on_weather (boost when TRUE)
- nearest_hospital_distance_km ≤30
weighting:
aat_certifying: 0.25
rescue_team_medical: 0.15
insurance: 0.10
instructor_cert: 0.15
equipment_freshness: 0.10
incident_history: 0.15
hospital_proximity: 0.10
user_band_handling:
fast: floor years_operating to 2
balanced: floor 3
great: AAT-recognized + 7+ yrs + 0 incidents past 12 months
Locked weights: time 0.15 / taste 0.25 / budget 0.20 / safety 0.40. Safety dominates — adventure has irreducible risk; the catalog must filter out under-insured / under-certified / under-rescued operators before TTBS even runs.
7. COMPLETION CONTRACT
POST /api/v1/cpc/mcp_provider/<your_partner_id>
Body:
{
"intent": "travel.book_adventure_activity",
"external_id": "<booking_id>",
"request_id": "<request_id>",
"amount_inr": 450, // NET partner commission only
"gst_inr": 81,
"tips_inr": 0,
"pass_through_inr": 4500, // remitted to operator (instructor + equipment + insurance + tickets)
"closed_at": "2026-10-12T13:00:00+05:30",
"status": "completed",
"activity_kind": "paragliding",
"activity_grade": "beginner",
"participants": 2,
"operator_id": "bir_skies",
"aat_recognized": true
}
User pays full fee. Partner's commission (~₹450) is amount_inr. Operator remittance is pass_through_inr. TOMO charges 10% × ₹450. HMAC-SHA256, 5-min replay.
Weather-cancel bookings file status: cancelled with cancellation_reason: weather_unsafe and amount_inr: 0 (no commission earned on weather cancels).
8. WIDGET
AdventureActivityWidget.
Field mapping:
- activity_kind + activity_grade pill
- operator.name + AAT badge when recognized
- certifying_bodies pill row
- rating.average_score + review_count → star pill
- pricing.total_for_party_inr → big price
- duration_hours → "1.5h activity"
- rescue_team_onsite → "Rescue team" badge
- insurance_covers_participants → "Insured ₹X cover" pill
- weather_policy.auto_refund_on_weather → "Weather refund" pill
- eligibility.contraindications (when user matches) → safety warning
9. CACHING POLICY
| Call | TTL | Rationale |
|---|---|---|
| search_activities | 90s | Slot availability moves |
| get_activity_detail | 30min | Briefing content stable; weather flagging volatile |
| hold_activity_slot | NO CACHE | Stateful lock |
| confirm_activity | NO CACHE | Idempotent |
| cancel_activity | NO CACHE | — |
| Operator AAT / certification verification | 7d | MoT updates monthly |
10. ERROR CODES
| Code | HTTP | Meaning | Retry |
|---|---|---|---|
INVALID_REQUEST |
400 | Malformed | No |
RATE_LIMITED |
429 | Throttle | 1, 2s |
INTERNAL_ERROR |
500 | Partner failure | 2, exp |
SIGNATURE_INVALID |
401 | HMAC fail | No |
NO_OPERATOR_MATCH |
200 (empty) | None | n/a |
SLOT_TAKEN |
409 | Hold race-lost | UI re-queries |
HOLD_EXPIRED |
410 | Hold elapsed | UI re-holds |
CERTIFICATION_EXPIRED |
422 | Instructor cert lapsed | No |
PARTICIPANT_INELIGIBLE |
422 | Health / age / weight fails | No (UI re-asks) |
CONTRAINDICATION_DISCLOSED |
422 | Pre-existing condition flagged | No (UI re-routes / refuses) |
EQUIPMENT_NOT_SERVICED |
422 | last_service older than 12 months | No (operator removed) |
INDEMNITY_NOT_SIGNED |
422 | indemnity_e_signed=false at confirm | No |
PAYMENT_FAILED |
402 | Gateway declined | No |
WEATHER_CANCEL |
200 (advisory) | Auto-full-refund | n/a |
EVACUATION_PLAN_MISSING |
422 | Operator without evac plan filtered | No |
CANCELLED_TOO_LATE |
410 | Past last_cancellable_at | No |
11. SANDBOX → PRODUCTION CHECKLIST
[ ] All five tools implemented
[ ] At least 30 operators across 10+ activity kinds + locations
[ ] Every AAT recognition verifiable on Ministry of Tourism's adventure list
[ ] Every certifying body (PADI / PGAI / ITRA etc.) license verifiable
[ ] Every instructor certification id verifiable; expiry checked
[ ] Equipment service-dates audited (sample of 5 operators)
[ ] Insurance per-participant cover ≥₹2,00,000 minimum (TOMO floor)
[ ] Rescue team + medical kit + hospital proximity validated for water / high-altitude
[ ] Indemnity form per BIS / industry-standard wording reviewed
[ ] Auto-refund on weather cancel tested end-to-end
[ ] Cancellation policy publishes before payment
[ ] HMAC signing verified on test CPC webhook
[ ] amount_inr in CPC is COMMISSION (NET); pass_through_inr is operator total
[ ] All controlled vocabularies respected
[ ] No forbidden fields anywhere
[ ] SLA p95 met
[ ] Privacy policy + grievance officer contact uploaded
[ ] Customer support: 24×7 adventure helpline + emergency escalation
[ ] Incident reporting pipeline verified (operator → TOMO → MoT)
[ ] Health declarations encrypted at rest; deleted 90 days post-activity
12. ANTI-FABRICATION RULES
RULE 1: AAT (Ministry of Tourism Approved Adventure Tour Operator)
recognition flag must match MoT public list. Re-checked weekly.
RULE 2: Certifying bodies (PADI / PGAI / ITRA / IFMGA / IMF etc.) must be
verifiable on the issuing body's register. Re-checked weekly.
RULE 3: Instructor certification id verifiable on certifying body's
register; expired = ingest reject.
RULE 4: incident_count_past_12_months is auditable; partner cannot suppress.
TOMO maintains a shadow incident log from MoT advisories + insurer
claim data + user reports.
RULE 5: Equipment last_service_dates must reflect documented service logs.
Service-log audits on 5 random operators per quarter.
RULE 6: rescue_team_onsite + medical_kit_certified are HARD filters for
scuba / paragliding / skydive / rafting / high-altitude trek;
partner false-flagging = customer-harm + suspension.
RULE 7: insurance_covers_participants must be ≥₹2,00,000 per participant;
below = ingest reject. Real insurer policy must be verifiable.
RULE 8: No paid_placement, ad_bid, sponsored_rank, featured_operator.
RULE 9: rating.average_score from completed sessions only; review_count
from verified bookings only.
RULE 10: No success_guarantee or "100% safe" text. Adventure has residual
risk; absolute claims forbidden. TOMO ingest scans for "100% safe",
"guaranteed safe", "zero risk."
RULE 11: amount_inr is partner COMMISSION only. Operator remittance is
pass_through_inr. Inflated commission base = audit.
RULE 12: AI-generated activity photos forbidden. Real recent photos required.
RULE 13: Health declarations are sensitive PII. Partner must encrypt at
rest, delete 90 days post-activity, never use for marketing.
RULE 14: Indemnity form MUST be e-signed before activity start. Operators
hand-signing onsite without TOMO booking link breaks audit trail.
RULE 15: Weather cancellations auto-refund full amount within 24h
regardless of policy windows; partner cannot block. TOMO audits.
RULE 16: Information-completeness score (hidden rank factor weight 0.10)
rewards full §4 shape.
RULE 17: Operators with incident in past 12 months MUST disclose;
concealment = suspension. Disclosed incidents trigger TOMO
editorial review (not ban, but downranked for 6 months).
VERSION HISTORY
v1.0.0 — 2026-05-14 — Initial spec. AAT-recognized operators with verified
certifying bodies (PADI/PGAI/ITRA/IFMGA/IMF).
Per-participant insurance ≥₹2L floor. Indemnity
e-signed pre-activity. Weather auto-refund.
Safety-dominant TTBS (0.40). Net-commission base.
Health declarations encrypted, 90-day deletion.