Intent Spec — entertainment.book_comedy_show
FULL ID: entertainment.book_comedy_show
VERSION: v1.0.0
STATUS: draft
LAST UPDATED: 2026-05-12
DOMAIN: entertainment
PRIMARY AGENT: EntertainmentAgent
TTBS WEIGHTS: time=0.20 taste=0.45 budget=0.20 safety=0.15
User books a ticket to a stand-up comedy show, improv night, or open-mic at a venue. TASTE-dominant because comedy is fundamentally about performer match — the user is choosing a comedian, not a movie or a seat class. Distinct from entertainment.book_theatre_play (scripted live theatre) and entertainment.book_movie_ticket (recorded film).
Partner exemplars: BookMyShow, Paytm Insider, District by Zomato, individual comedy clubs (Habitat Mumbai, The Comedy Cellar, Punchliners), comic-direct sites (Naveen Polishetty's site, OML Insider).
SECTION 1 — INTENT IDENTITY
User wants to attend live comedy. Distinct from:
entertainment.book_movie_ticket— recorded filmentertainment.book_theatre_play— scripted theatre/dramaentertainment.book_concert_ticket— music concertentertainment.book_event_venue— booking the venue (not a ticket to a show in it)- Streaming comedy specials →
entertainment.subscribe_streaming(Netflix/Prime/SonyLIV)
Single intent per booking. Group bookings (≤20 seats) classify here; larger corporate book-outs escalate to entertainment.book_event_venue.
SECTION 2 — NATURAL LANGUAGE COVERAGE
CLASSIFIES IN
- "Book Zakir Khan tickets for Friday"
- "Stand-up comedy near me tonight"
- "Naveen Polishetty live show in Hyderabad"
- "Two tickets for Comicstaan tour"
- "Open mic this weekend"
- "Improv comedy at Habitat"
- "Telugu stand-up show this Saturday"
- "Tanmay Bhat live, get me the cheapest seat"
- "Funniest show in town tomorrow"
- "Comedy roast tickets"
CLASSIFIES OUT — BORDERLINE NO
- "Watch a movie" →
entertainment.book_movie_ticket - "Music concert" →
entertainment.book_concert_ticket - "Drama / theatre play" →
entertainment.book_theatre_play - "Book a venue for my friend's comedy event" →
entertainment.book_event_venue - "Watch comedy on Netflix" →
entertainment.subscribe_streaming
MULTI-INTENT TRIGGERS
- "Comedy show + dinner nearby" →
entertainment.book_comedy_show+food.book_dine_in - "Comedy + ride home after" →
entertainment.book_comedy_show+mobility.book_intracity_ride(pickup time = show end)
SECTION 3 — INPUT (TOMO → PROVIDER)
{
"intent": "entertainment.book_comedy_show",
"request_id": "req_01J9Z...",
"user_locale": "en-IN",
"user_location": { "lat": 17.4475, "lng": 78.3563, "max_radius_km": 18, "city": "Hyderabad" },
"preferences": {
"comedian_name": "Zakir Khan", // OR null = open to any
"language": ["hi", "en"], // BCP-47, ordered preference
"show_format": ["stand_up"], // STRICT ENUM §6
"content_rating_max": "adult_18", // STRICT ENUM §6 (CBFC-style; some comedy is 18+)
"showtime_window": {
"start": "2026-05-15T18:00:00+05:30",
"end": "2026-05-15T23:30:00+05:30"
},
"seat_count": 2,
"seat_section_preference": ["premium", "standard"],
"alcohol_serving_acceptable": true,
"accessibility": { "wheelchair_seats_required": 0 }
},
"ttbs_user_band": { "time": "balanced", "taste": "good", "budget": "good", "safety": "balanced" },
"session_context": { "tomo_session_id": "ses_01J9Z...", "user_dna_hash": "dna_v3_a7c9..." }
}
| Field | Type | Constraint | Notes |
|---|---|---|---|
intent |
string | REQUIRED, STRICT ENUM | Always entertainment.book_comedy_show |
preferences.comedian_name |
string | null | REQUIRED nullable | Null = open; partner returns top shows in window |
preferences.language |
array |
REQUIRED, BCP-47, ≥1 | |
preferences.show_format |
array |
REQUIRED, ≥1, STRICT ENUM §6 | |
preferences.content_rating_max |
enum | REQUIRED, STRICT ENUM §6 | Adult-only shows filtered out when max=U |
preferences.seat_count |
int | REQUIRED, 1-20 | >20 → escalate to event_venue |
preferences.alcohol_serving_acceptable |
bool | REQUIRED | Some venues are dry; affects filter |
Anti-fabrication preamble: Provider may not list a show with a comedian who has not confirmed the date. Surge multipliers must be disclosed.
SECTION 4 — PROVIDER TOOLS
Tool 1: search_comedy_shows
PURPOSE: Up to 20 shows matching window + comedian + format
SLA: p50 ≤ 600ms, p95 ≤ 1500ms, p99 ≤ 3000ms
RATE LIMIT: 60 req/min
IDEMPOTENCY: request_id; 30s cache
RETRY: 1 on 429, 2 on 5xx
Tool 2: get_seat_map
PURPOSE: Live seat availability for a chosen show
SLA: p50 ≤ 300ms, p95 ≤ 800ms
RATE LIMIT: 120 req/min
RETRY: 1 on 5xx
Tool 3: create_booking
PURPOSE: Lock + confirm seats
SLA: p50 ≤ 1500ms, p95 ≤ 4000ms
RATE LIMIT: 30 req/min
IDEMPOTENCY: request_id
RETRY: No retry
Tool 4: cancel_booking
SLA: p50 ≤ 1000ms, p95 ≤ 3000ms
RATE LIMIT: 30 req/min
RETRY: 1 on 5xx
SECTION 5 — RESPONSE SHAPE
ComedyShowListing
ComedyShowListing:
show_id: { type: string, constraint: REQUIRED }
show:
title: { type: string, constraint: REQUIRED, semantics: "official show title" }
tour_name: { type: string, constraint: REQUIRED nullable, semantics: "e.g. 'Manpasand', 'Tathastu'" }
comedians:
type: array<Comedian>
constraint: REQUIRED, ≥1
shape:
name: { type: string, constraint: REQUIRED }
instagram_handle: { type: string, constraint: REQUIRED nullable }
verified: { type: boolean, constraint: REQUIRED, semantics: "partner has confirmed date with talent" }
show_format: { type: enum, constraint: REQUIRED, STRICT ENUM §6 }
language: { type: enum, constraint: REQUIRED, BCP-47 }
duration_minutes: { type: int, constraint: REQUIRED, 30-240 }
content_rating: { type: enum, constraint: REQUIRED, STRICT ENUM §6 }
content_warnings:
type: array<enum>
constraint: REQUIRED, may be empty
values: [strong_language, sexual_content, political, religious, dark_humour, audience_participation]
venue:
venue_id: { type: string, constraint: REQUIRED }
name: { type: string, constraint: REQUIRED }
venue_type:
type: enum
constraint: REQUIRED, STRICT ENUM §6
values: [comedy_club, theatre, bar_with_stage, hotel_ballroom, open_air, auditorium]
address: { type: string, constraint: REQUIRED }
location: { type: object, shape: { lat, lng }, constraint: REQUIRED }
distance_from_user_km: { type: float, constraint: REQUIRED, 0-50 }
alcohol_served: { type: boolean, constraint: REQUIRED }
food_served: { type: boolean, constraint: REQUIRED }
parking_available: { type: boolean, constraint: REQUIRED }
accessibility:
wheelchair_accessible: { type: boolean, constraint: REQUIRED }
hearing_loop: { type: boolean, constraint: REQUIRED }
showtime:
start: { type: string, constraint: REQUIRED, ISO_DATETIME }
end: { type: string, constraint: REQUIRED, ISO_DATETIME }
advance_booking_cutoff: { type: string, constraint: REQUIRED, ISO_DATETIME }
doors_open_minutes_before: { type: int, constraint: REQUIRED, 0-120 }
pricing:
sections:
type: array<SectionPrice>
constraint: REQUIRED, ≥1
shape:
section_id: { type: string, constraint: REQUIRED }
section_label: { type: enum, constraint: REQUIRED, STRICT ENUM §6, values: [standard, premium, vip, fan_pit, meet_and_greet] }
base_price_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
convenience_fee_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
gst_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
total_per_seat_inr: { type: int, constraint: REQUIRED, INR_INTEGER }
surge_active: { type: boolean, constraint: REQUIRED }
surge_multiplier: { type: float, constraint: REQUIRED when surge_active=true, 1.0-3.0 }
availability:
seats_available_total: { type: int, constraint: REQUIRED, ≥0 }
seats_available_by_section: { type: object<section_id,int>, constraint: REQUIRED }
fast_selling: { type: boolean, constraint: REQUIRED, semantics: "TRUE only when seats_available_total / seats_total_capacity < 0.20" }
policies:
cancellation:
cutoff_minutes_before_start: { type: int, constraint: REQUIRED, ≥0 }
refund_percent: { type: int, constraint: REQUIRED, 0-100 }
age_restriction_enforced: { type: boolean, constraint: REQUIRED }
photography_allowed: { type: boolean, constraint: REQUIRED }
re_entry_allowed: { type: boolean, constraint: REQUIRED }
partner_reference:
source: { type: string, constraint: REQUIRED }
deeplink: { type: string, constraint: REQUIRED, HTTPS URL }
SeatMap, Booking, CancellationResult — same shape as entertainment.book_movie_ticket §5; see that spec for full schemas.
FORBIDDEN FIELDS
paid_placement_score,ad_bid,sponsored_rank,promotion_priority,kickback_amountartificial_urgency_text(no "Hurry!" without numeric inventory backing)ai_generated_photofor comedian / venue imagerycommission_padded_priceunverified_talent_listing— listings whereverified=falsecannot be ranked above verified ones
SECTION 6 — CONTROLLED VOCABULARIES
preferences.show_format:
values:
stand_up: "Solo stand-up comedy"
sketch_comedy: "Sketch / character comedy"
improv: "Improvisational comedy"
open_mic: "Open mic / mixed performers"
roast: "Roast event"
podcast_live: "Live podcast recording"
comedy_festival: "Multi-comic festival lineup"
content_rating_max / content_rating:
values:
U: "Universal — family-friendly"
UA: "Parental guidance under 12"
adult_16: "Adult content, 16+"
adult_18: "Adult content, 18+"
venue.venue_type:
values: { comedy_club, theatre, bar_with_stage, hotel_ballroom, open_air, auditorium }
pricing.sections.section_label:
values:
standard: "Standard seating"
premium: "Premium (closer / better view)"
vip: "VIP (front rows / reserved area)"
fan_pit: "Standing room near stage"
meet_and_greet: "Includes post-show meet & greet"
content_warnings:
values: { strong_language, sexual_content, political, religious, dark_humour, audience_participation }
SECTION 7 — TTBS DIMENSIONS
TIME (weight = 0.20):
signals_used: [distance, showtime fit, advance_booking_cutoff proximity]
weighting: { distance: 0.30, fit: 0.50, cutoff_proximity: 0.20 }
TASTE (weight = 0.45):
signals_used:
- comedian_name exact match
- language match
- show_format match
- content_rating ≤ user max
- DNA history (comedians/venues user has booked before)
weighting:
comedian: 0.45
format: 0.20
language: 0.20
rating_ok: 0.10
venue_familiarity: 0.05
user_band_handling:
fast: relax exact comedian match
balanced: standard
flexible: widen show_format set
BUDGET (weight = 0.20):
signals_used: [pricing min, surge_active]
weighting: { base_price: 0.70, surge_penalty: 0.30 }
SAFETY (weight = 0.15):
signals_used:
- venue.accessibility match
- policies.age_restriction_enforced (when user_dna indicates minor in party)
- comedians[].verified (FALSE = filter out)
weighting:
accessibility: 0.40
age_appropriate: 0.30
verified_talent: 0.30
SECTION 8 — COMPLETION CONTRACT
POST /api/v1/cpc/mcp_provider/<your_partner_id>
Body:
{
"intent": "entertainment.book_comedy_show",
"external_id": "<booking_id>",
"request_id": "<request_id>",
"amount_inr": 720, // NET (base × seat_count + supplier-kept convenience)
"gst_inr": 130,
"tips_inr": 0,
"pass_through_inr": 0,
"closed_at": "2026-05-15T20:30:00+05:30",
"status": "completed",
"seat_count": 2,
"show_format": "stand_up",
"comedian_name": "Zakir Khan"
}
HMAC, 5-min replay, NET-only commission.
SECTION 9 — WIDGET
ComedyShowWidget (planned). Falls back to generic ListingsWidget.
Field mapping:
- show.tour_name + comedians[0].name → header
- venue.name + distance_from_user_km → subline 1
- showtime.start + show.duration_minutes + content_rating → subline 2
- pricing.sections[min].total_per_seat_inr → "From ₹X"
- availability.fast_selling → red "Fast selling" pill (only when true AND ratio < 20%)
- comedians[].verified=true → verified-tick badge on poster
SECTION 10 — CACHING POLICY
| Call | TTL |
|---|---|
| search_comedy_shows | 60s |
| get_seat_map | NO CACHE |
| create_booking | NO CACHE |
| cancel_booking | NO CACHE |
| Comedian / venue metadata | 6h |
SECTION 11 — 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 (webhook) | HMAC fail | No |
NO_SHOWS_IN_WINDOW |
200 (empty) | Valid, no matches | n/a |
COMEDIAN_NOT_TOURING |
200 (empty) | Requested comedian has no shows in window | n/a |
SHOW_SOLD_OUT |
409 (create) | Seats gone | No |
SEATS_PARTIALLY_UNAVAILABLE |
409 (create) | Partial availability returned | No |
BOOKING_WINDOW_CLOSED |
410 (create) | Past advance_booking_cutoff | No |
AGE_VERIFICATION_FAILED |
403 (create) | adult_18 + minor in party | No |
CANCELLATION_WINDOW_CLOSED |
410 (cancel) | Past cancellation cutoff | No |
SECTION 12 — SANDBOX → PRODUCTION CHECKLIST
[ ] All four tools implemented; shapes per §5
[ ] At least 2 real venues + 5 real shows in window
[ ] comedians[].verified=true backed by confirmation evidence (email/DM screenshot from talent)
[ ] All controlled vocabularies respected
[ ] HMAC signing verified
[ ] amount_inr is NET in CPC
[ ] No forbidden fields
[ ] SLA p95 met
[ ] Idempotency tested
[ ] Compliance docs: GSTIN, entertainment license, alcohol license where applicable, privacy policy URL
SECTION 13 — ANTI-FABRICATION RULES
RULE 1: No paid_placement / ad / kickback. Single occurrence rejects response.
RULE 2: comedians[].verified=true requires evidence on file (talent confirmation,
contract, or talent-agent confirmation). Listings of unverified or
speculatively-listed talent = suspension after first complaint.
RULE 3: fast_selling=true ONLY when seats_available_total / seats_total_capacity < 0.20.
Same enforcement as movie_ticket.
RULE 4: surge_multiplier must be a real number reflecting actual markup. Hidden
surge = suspension.
RULE 5: AI-generated comedian photos forbidden. Use official press/social shots
under licence.
RULE 6: content_rating must be honest. Listing an 18+ show as U to capture broader
search = customer harm = suspension.
RULE 7: Reviews / ratings (if added in v1.1) must be unrounded floats from real
attendees.
RULE 8: No "Top Pick" / "TOMO Recommended" badges. TTBS source-blind.
RULE 9: artificial_urgency forbidden. fast_selling boolean is the only allowed
scarcity signal.
RULE 10: content_warnings must be honestly disclosed. Hiding political /
religious / sexual_content warnings = customer harm + suspension.
VERSION HISTORY
v1.0.0 — 2026-05-12 — Initial spec. NET commission base. TASTE-dominant (0.45) —
comedy is a performer-match decision.