Developer Docs

Entertainment & Experiences

The Entertainment vertical powers arcades, escape rooms, event venues, and entertainment centers with four integrated systems: session booking, event ticketing, digital credits, and gamified loyalty. Malet Owners configure these features via the Storefront Window layout system.

NOTE

These features live in the experiences subgraph โ€” a cross-vertical Feature Plane designed for progressive extraction. Future verticals (Culture, Wellness, Tech) will share these modules. The architecture is documented in docs/architecture/18-progressive-vertical-extraction.md.


Architecture: Two-Plane Design

The Entertainment vertical uses a Control + Feature plane separation:

  • Control Plane (malets subgraph): VerticalConfig feature flags decide which Entertainment modules are enabled per Malet โ€” eventTicketing, digitalCredits, gamifiedLoyalty, sessionBooking.
  • Feature Plane (experiences subgraph): Self-contained modules for Events, Credit Wallets, and Loyalty. Each module can be extracted to its own subgraph when dedicated vertical Teams form.
  • Extensions (services subgraph): Session booking fields on the existing Service and Booking entities.

Three new Storefront Window layout slot types are available: EVENT_CALENDAR, CREDIT_STORE, and LEADERBOARD.


Event Ticketing

Create events with tiered pricing, automatic capacity tracking, and QR-code check-in at venues.

Event Lifecycle

DRAFT โ†’ ON_SALE โ†’ SOLD_OUT โ†’ CANCELLED
                            โ†’ COMPLETED

Events start as DRAFT (invisible to Visitors). The Malet Owner calls publishEvent to begin ticket sales. When all tier capacity is consumed, the event auto-transitions to SOLD_OUT.

Mutations

# Create an event (Malet Owner only)
mutation {
	createEvent(
		input: {
			maletId: "m_luminara"
			title: "Friday Game Night"
			description: "Weekly competitive gaming tournament"
			startDate: "2026-05-01T19:00:00Z"
			endDate: "2026-05-01T23:00:00Z"
			tiers: [
				{ name: "General", price: 1500, capacity: 50 }
				{ name: "VIP", price: 3500, capacity: 10 }
			]
			venue: { name: "Arena Floor", address: "123 Mall Ave" }
		}
	) {
		id
		slug
		status
	}
}

# Publish โ€” starts ticket sales
mutation {
	publishEvent(id: "evt_1") {
		id
		status
	}
}

# Visitor purchases a ticket โ€” atomic capacity check
mutation {
	purchaseEventTicket(input: { eventId: "evt_1", tierId: "vip", userId: "v_meekdenzo" }) {
		id
		ticketCode # "TK-A7K3M9X2B1" โ€” for QR scanning
		status
	}
}

# Scan QR at venue entry
mutation {
	checkInEventTicket(ticketCode: "TK-A7K3M9X2B1") {
		id
		status
	}
}

Queries

# Upcoming events for a Malet
query {
	upcomingEvents(maletId: "m_luminara") {
		id
		title
		startDate
		tiers {
			name
			price
			capacity
			soldCount
		}
	}
}

# Single event by slug (for Malet storefront URLs)
query {
	eventBySlug(maletId: "m_luminara", slug: "friday-game-night") {
		id
		title
		description
		tiers {
			name
			price
			capacity
			soldCount
		}
		venue {
			name
			address
		}
	}
}

Ticket Codes

Each ticket receives a unique TK-{code} identifier for QR scanning โ€” consistent with the BK-{code} pattern used for booking confirmation codes in the services subgraph.


Digital Credit Wallets

Per-Visitor, per-Malet prepaid credit wallets for in-venue spending (arcade tokens, game credits, etc.).

How Credits Work

  1. Purchase โ€” Visitor buys a credit pack via uCart (e.g., 100 tokens for $20)
  2. Spend โ€” Credits are deducted per game or activity
  3. Balance โ€” Real-time balance query via myCreditBalance

TIP

Credit packs will be purchasable as a CREDIT_PACK product type through uCart in a future release. For now, Malet Owners can award credits manually via addCredits.

Transaction Types

Type Description
PURCHASE Bought credit pack via uCart
SPEND Deducted for game or activity
REFUND Credits returned for cancelled booking
BONUS Welcome bonus or promotional credits
ADJUSTMENT Manual Malet Owner adjustment

Mutations

# Malet Owner adds credits to Visitor's wallet
mutation {
	addCredits(
		maletId: "m_luminara"
		userId: "v_meekdenzo"
		amount: 100
		type: PURCHASE
		description: "100 Token Pack"
	) {
		balance
	}
}

# Malet Owner deducts credits (with balance guard)
mutation {
	deductCredits(
		maletId: "m_luminara"
		userId: "v_meekdenzo"
		amount: 30
		description: "Bowling Game"
	) {
		balance
	}
}

# Visitor tops up own credits
mutation {
	topUpCredits(maletId: "m_luminara", amount: 50) {
		balance
	}
}

Queries

# Visitor checks their wallet
query {
	myCreditWallet(maletId: "m_luminara") {
		balance
		transactions {
			type
			amount
			description
			createdAt
		}
	}
}

# Quick balance check
query {
	myCreditBalance(maletId: "m_luminara") # Returns: 70
}

Gamified Loyalty

Reward repeat Visitors with points, tier promotions, and achievement badges.

Tier System

Malet Owners configure custom tiers with automatic promotion. When a Visitor earns enough points, their tier is upgraded automatically:

Tier Points Required Example Perks
Bronze 100 5% credit bonus
Silver 500 10% credit bonus, priority booking
Gold 1,000 20% credit bonus, VIP access, free birthday game

Achievements

Award badges for milestones โ€” idempotent ($addToSet ensures duplicate awards are safely ignored):

  • played-10-games
  • first-visit
  • birthday-booking

Mutations

# Configure loyalty program (Malet Owner)
mutation {
	configureLoyalty(
		maletId: "m_luminara"
		input: {
			pointsPerPurchase: 10
			pointsPerVisit: 5
			tiers: [
				{ name: "Bronze", minPoints: 100 }
				{ name: "Silver", minPoints: 500 }
				{ name: "Gold", minPoints: 1000 }
			]
		}
	) {
		id
	}
}

# Award points (auto-promotes tier)
mutation {
	awardLoyaltyPoints(
		maletId: "m_luminara"
		userId: "v_meekdenzo"
		points: 50
		reason: "Murchase completed"
	) {
		totalPoints
		tier
	}
}

# Record a visit
mutation {
	recordVisit(maletId: "m_luminara") {
		visitCount
		totalPoints
	}
}

# Award achievement badge
mutation {
	awardAchievement(maletId: "m_luminara", userId: "v_meekdenzo", slug: "first-visit") {
		achievements {
			slug
			awardedAt
		}
	}
}

Queries

# Visitor's loyalty state
query {
	myLoyalty(maletId: "m_luminara") {
		totalPoints
		currentPoints
		tier
		visitCount
		achievements {
			slug
			awardedAt
		}
	}
}

# Top Visitors leaderboard
query {
	loyaltyLeaderboard(maletId: "m_luminara", limit: 10) {
		userId
		totalPoints
		tier
	}
}

Session Booking

Extended booking fields for entertainment venues on the existing services subgraph:

Field Description
sessionType INDIVIDUAL, GROUP, or OPEN_PLAY
maxParticipants Capacity per session (e.g., 6 for escape room)
groupBookingEnabled Allow multi-Visitor reservations
checkInStatus NOT_CHECKED_IN โ†’ CHECKED_IN / NO_SHOW
checkInCode Unique code for QR scan at venue

These fields extend the existing Service and Booking entities โ€” no new subgraph needed.