# Demo Session Service ## Overview The **Demo Session Service** creates ephemeral, isolated demo environments for sales demonstrations and prospect trials. It provisions temporary tenants with pre-seeded realistic bakery data, allowing prospects to explore the full platform without affecting production data. Demo sessions automatically expire after a configurable period (default: 24 hours) and are completely isolated from real customer tenants, making it safe for prospects to experiment freely. ## Key Features ### Demo Environment Provisioning - **One-Click Demo Creation** - Create demo tenant in seconds - **Pre-Seeded Data** - Realistic sales, inventory, forecast data - **Isolated Tenants** - Complete separation from production - **Temporary Credentials** - Auto-generated demo user accounts - **Configurable Duration** - 1 hour to 7 days (default: 24 hours) - **Instant Access** - No email verification required ### Realistic Demo Data - **90 Days Sales History** - Realistic transaction patterns - **Product Catalog** - 20+ common bakery products - **Inventory** - Current stock levels and movements - **Forecasts** - Pre-generated 7-day forecasts - **Production Schedules** - Sample production plans - **Suppliers** - 5+ sample supplier profiles - **Team Members** - Sample staff with different roles ### Demo Scenarios - **Standard Bakery** - Small neighborhood bakery (1 location) - **Multi-Location** - Bakery chain (3 locations) - **High-Volume** - Large production bakery - **Custom Scenario** - Configurable for specific prospects - **Spanish Locale** - Madrid-based bakery examples - **Feature Showcase** - Highlight specific capabilities ### Session Management - **Auto-Expiration** - Automatic cleanup after expiry - **Session Extension** - Extend active demos - **Session Termination** - Manually end demo - **Session Analytics** - Track demo engagement - **Concurrent Limits** - Prevent resource abuse - **IP-Based Tracking** - Monitor demo usage ### Sales Enablement - **Demo Link Generation** - Shareable demo URLs - **Sales Dashboard** - Track active demos - **Usage Analytics** - Feature engagement metrics - **Lead Tracking** - Connect demos to CRM - **Conversion Tracking** - Demo to trial to paid - **Performance Metrics** - Demo success rates ### Security & Isolation - **Tenant Isolation** - Complete data separation - **Resource Limits** - Prevent abuse - **Auto-Cleanup** - Remove expired demos - **No Production Access** - Isolated database/environment - **Rate Limiting** - Prevent demo spam - **Audit Logging** - Track all demo activities ## Business Value ### For Sales Team - **Instant Demos** - No setup time, always ready - **Realistic Experience** - Prospects see real functionality - **Risk-Free** - Prospects can't break anything - **Consistent** - Every demo shows same quality data - **Scalable** - Handle 100+ concurrent demos - **Self-Service** - Prospects can explore independently ### Quantifiable Impact - **Sales Cycle**: 30-50% shorter with live demos - **Conversion Rate**: 2-3× higher vs. screenshots/videos - **Demo Setup Time**: 0 minutes vs. 15-30 minutes manual - **Lead Quality**: Higher engagement indicates serious interest - **Sales Efficiency**: 5-10× more demos per sales rep - **Cost Savings**: €500-1,500/month (sales time saved) ### For Prospects - **Try Before Buy**: Experience platform hands-on - **No Commitment**: No credit card, no sign-up friction - **Immediate Access**: Start exploring in 30 seconds - **Realistic Data**: Understand real-world value - **Self-Paced**: Explore at own speed - **Safe Environment**: Can't break or affect anything ## Technology Stack - **Framework**: FastAPI (Python 3.11+) - Async web framework - **Database**: PostgreSQL 17 - Demo session tracking - **Demo DB**: Separate PostgreSQL - Isolated demo data - **Caching**: Redis 7.4 - Session cache, rate limiting - **Messaging**: RabbitMQ 4.1 - Cleanup events - **Data Seeding**: Faker, custom data generators - **ORM**: SQLAlchemy 2.0 (async) - Database abstraction - **Logging**: Structlog - Structured JSON logging - **Metrics**: Prometheus Client - Demo metrics ## API Endpoints (Key Routes) ### Demo Session Management - `POST /api/v1/demo-sessions` - Create new demo session - `GET /api/v1/demo-sessions/{session_id}` - Get session details - `POST /api/v1/demo-sessions/{session_id}/extend` - Extend session - `DELETE /api/v1/demo-sessions/{session_id}` - Terminate session - `GET /api/v1/demo-sessions/{session_id}/credentials` - Get login credentials - `GET /api/v1/demo-sessions/active` - List active sessions ### Demo Scenarios - `GET /api/v1/demo-sessions/scenarios` - List available scenarios - `GET /api/v1/demo-sessions/scenarios/{scenario_id}` - Get scenario details - `POST /api/v1/demo-sessions/scenarios/{scenario_id}/create` - Create session from scenario ### Sales Dashboard (Internal) - `GET /api/v1/demo-sessions/analytics/dashboard` - Demo analytics - `GET /api/v1/demo-sessions/analytics/usage` - Usage patterns - `GET /api/v1/demo-sessions/analytics/conversion` - Demo to signup conversion ### Health & Monitoring - `GET /api/v1/demo-sessions/health` - Service health - `GET /api/v1/demo-sessions/cleanup/status` - Cleanup job status ## Database Schema ### Main Tables **demo_sessions** ```sql CREATE TABLE demo_sessions ( id UUID PRIMARY KEY, session_token VARCHAR(255) UNIQUE NOT NULL, demo_tenant_id UUID NOT NULL, -- Demo tenant in separate DB -- Configuration scenario_name VARCHAR(100) NOT NULL, -- standard_bakery, multi_location, etc. duration_hours INTEGER DEFAULT 24, -- Status status VARCHAR(50) DEFAULT 'active', -- active, extended, expired, terminated created_at TIMESTAMP DEFAULT NOW(), expires_at TIMESTAMP NOT NULL, extended_count INTEGER DEFAULT 0, terminated_at TIMESTAMP, termination_reason VARCHAR(255), -- Tracking created_by_ip INET, user_agent TEXT, referrer VARCHAR(500), utm_source VARCHAR(100), utm_campaign VARCHAR(100), utm_medium VARCHAR(100), -- Usage analytics login_count INTEGER DEFAULT 0, last_activity_at TIMESTAMP, page_views INTEGER DEFAULT 0, features_used JSONB, -- Array of feature names -- Lead info (if provided) lead_email VARCHAR(255), lead_name VARCHAR(255), lead_phone VARCHAR(50), lead_company VARCHAR(255), INDEX idx_sessions_status ON (status, expires_at), INDEX idx_sessions_token ON (session_token) ); ``` **demo_scenarios** ```sql CREATE TABLE demo_scenarios ( id UUID PRIMARY KEY, scenario_name VARCHAR(100) UNIQUE NOT NULL, display_name VARCHAR(255) NOT NULL, description TEXT, -- Configuration business_name VARCHAR(255), location_count INTEGER DEFAULT 1, product_count INTEGER DEFAULT 20, days_of_history INTEGER DEFAULT 90, -- Features to highlight featured_capabilities JSONB, -- Data generation settings seed_data_config JSONB, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT NOW() ); ``` **demo_session_events** ```sql CREATE TABLE demo_session_events ( id UUID PRIMARY KEY, session_id UUID REFERENCES demo_sessions(id) ON DELETE CASCADE, event_type VARCHAR(100) NOT NULL, -- login, page_view, feature_used, action_performed event_data JSONB, ip_address INET, occurred_at TIMESTAMP DEFAULT NOW(), INDEX idx_session_events_session (session_id, occurred_at) ); ``` **demo_session_metrics** ```sql CREATE TABLE demo_session_metrics ( id UUID PRIMARY KEY, metric_date DATE NOT NULL, scenario_name VARCHAR(100), -- Volume sessions_created INTEGER DEFAULT 0, sessions_completed INTEGER DEFAULT 0, -- Not terminated early sessions_expired INTEGER DEFAULT 0, sessions_terminated INTEGER DEFAULT 0, -- Engagement avg_duration_minutes INTEGER, avg_login_count DECIMAL(5, 2), avg_page_views DECIMAL(5, 2), avg_features_used DECIMAL(5, 2), -- Conversion demo_to_signup_count INTEGER DEFAULT 0, conversion_rate_percentage DECIMAL(5, 2), calculated_at TIMESTAMP DEFAULT NOW(), UNIQUE(metric_date, scenario_name) ); ``` ### Indexes for Performance ```sql CREATE INDEX idx_sessions_expires ON demo_sessions(expires_at) WHERE status = 'active'; CREATE INDEX idx_sessions_scenario ON demo_sessions(scenario_name, created_at DESC); CREATE INDEX idx_events_session_type ON demo_session_events(session_id, event_type); ``` ## Business Logic Examples ### Demo Session Creation ```python async def create_demo_session( scenario_name: str = 'standard_bakery', duration_hours: int = 24, lead_info: dict = None, request_info: dict = None ) -> DemoSession: """ Create new demo session with pre-seeded data. """ # Get scenario configuration scenario = await db.query(DemoScenario).filter( DemoScenario.scenario_name == scenario_name, DemoScenario.is_active == True ).first() if not scenario: raise ValueError("Invalid scenario") # Check concurrent demo limit active_demos = await db.query(DemoSession).filter( DemoSession.status == 'active', DemoSession.expires_at > datetime.utcnow() ).count() if active_demos >= MAX_CONCURRENT_DEMOS: raise Exception("Maximum concurrent demos reached") try: # Generate session token session_token = secrets.token_urlsafe(32) # Create demo tenant in separate database demo_tenant = await create_demo_tenant(scenario) # Seed demo data await seed_demo_data(demo_tenant.id, scenario) # Create session record session = DemoSession( session_token=session_token, demo_tenant_id=demo_tenant.id, scenario_name=scenario_name, duration_hours=duration_hours, expires_at=datetime.utcnow() + timedelta(hours=duration_hours), created_by_ip=request_info.get('ip'), user_agent=request_info.get('user_agent'), referrer=request_info.get('referrer'), utm_source=request_info.get('utm_source'), utm_campaign=request_info.get('utm_campaign'), lead_email=lead_info.get('email') if lead_info else None, lead_name=lead_info.get('name') if lead_info else None ) db.add(session) # Log event event = DemoSessionEvent( session_id=session.id, event_type='session_created', event_data={'scenario': scenario_name}, ip_address=request_info.get('ip') ) db.add(event) await db.commit() logger.info("Demo session created", session_id=str(session.id), scenario=scenario_name, duration_hours=duration_hours) # Publish event await publish_event('demo_sessions', 'demo.session_created', { 'session_id': str(session.id), 'scenario': scenario_name }) return session except Exception as e: logger.error("Demo session creation failed", scenario=scenario_name, error=str(e)) raise async def create_demo_tenant(scenario: DemoScenario) -> DemoTenant: """ Create isolated demo tenant in demo database. """ # Use separate database connection for demo data demo_db = get_demo_database_connection() tenant = DemoTenant( tenant_name=scenario.business_name or "Demo Bakery", email=f"demo_{uuid.uuid4().hex[:8]}@bakery-ia.com", status='demo', subscription_tier='pro', # Always show Pro features in demo is_demo=True ) demo_db.add(tenant) await demo_db.commit() return tenant async def seed_demo_data(tenant_id: UUID, scenario: DemoScenario): """ Seed demo tenant with realistic data. """ demo_db = get_demo_database_connection() # Seed configuration config = scenario.seed_data_config or {} product_count = config.get('product_count', 20) days_of_history = config.get('days_of_history', 90) # 1. Seed product catalog products = await seed_products(demo_db, tenant_id, product_count) # 2. Seed suppliers suppliers = await seed_suppliers(demo_db, tenant_id, 5) # 3. Seed inventory await seed_inventory(demo_db, tenant_id, products, suppliers) # 4. Seed sales history (90 days) await seed_sales_history(demo_db, tenant_id, products, days_of_history) # 5. Generate forecasts await seed_forecasts(demo_db, tenant_id, products) # 6. Seed production schedules await seed_production_schedules(demo_db, tenant_id, products) # 7. Seed team members await seed_team_members(demo_db, tenant_id) logger.info("Demo data seeded", tenant_id=str(tenant_id), products=len(products), suppliers=len(suppliers)) async def seed_sales_history( demo_db, tenant_id: UUID, products: list, days: int = 90 ) -> list: """ Generate realistic sales history using patterns. """ from faker import Faker fake = Faker('es_ES') # Spanish locale sales_records = [] start_date = date.today() - timedelta(days=days) for day_offset in range(days): current_date = start_date + timedelta(days=day_offset) is_weekend = current_date.weekday() >= 5 is_holiday = await is_spanish_holiday(current_date) # Adjust volume based on day type base_transactions = 50 if is_weekend: base_transactions = int(base_transactions * 1.4) # 40% more on weekends if is_holiday: base_transactions = int(base_transactions * 0.7) # 30% less on holidays # Add randomness daily_transactions = int(base_transactions * random.uniform(0.8, 1.2)) for _ in range(daily_transactions): # Random product product = random.choice(products) # Realistic quantity (most orders are 1-5 units) quantity = random.choices([1, 2, 3, 4, 5, 6, 10], weights=[40, 25, 15, 10, 5, 3, 2])[0] # Calculate price with small variance unit_price = product.price * random.uniform(0.95, 1.05) sale = DemoSale( tenant_id=tenant_id, sale_date=current_date, sale_time=fake.time(), product_id=product.id, product_name=product.name, quantity=quantity, unit_price=unit_price, total_amount=quantity * unit_price, channel='pos' ) sales_records.append(sale) # Bulk insert demo_db.bulk_save_objects(sales_records) await demo_db.commit() return sales_records ``` ### Auto-Cleanup Job ```python async def cleanup_expired_demos(): """ Background job to cleanup expired demo sessions. Runs every hour. """ # Find expired sessions expired_sessions = await db.query(DemoSession).filter( DemoSession.status == 'active', DemoSession.expires_at <= datetime.utcnow() ).all() for session in expired_sessions: try: # Mark session as expired session.status = 'expired' session.terminated_at = datetime.utcnow() # Delete demo tenant and all data await delete_demo_tenant(session.demo_tenant_id) # Log event event = DemoSessionEvent( session_id=session.id, event_type='session_expired', occurred_at=datetime.utcnow() ) db.add(event) logger.info("Demo session cleaned up", session_id=str(session.id), duration_hours=(session.terminated_at - session.created_at).total_seconds() / 3600) except Exception as e: logger.error("Demo cleanup failed", session_id=str(session.id), error=str(e)) continue await db.commit() logger.info("Demo cleanup completed", expired_count=len(expired_sessions)) ``` ## Events & Messaging ### Published Events (RabbitMQ) **Exchange**: `demo_sessions` **Routing Keys**: `demo.session_created`, `demo.session_converted` **Demo Session Created Event** ```json { "event_type": "demo_session_created", "session_id": "uuid", "scenario": "standard_bakery", "duration_hours": 24, "lead_email": "prospect@example.com", "utm_source": "google_ads", "timestamp": "2025-11-06T10:00:00Z" } ``` **Demo Converted to Signup** ```json { "event_type": "demo_session_converted", "session_id": "uuid", "tenant_id": "uuid", "scenario": "standard_bakery", "demo_duration_hours": 2.5, "features_used": ["forecasting", "inventory", "production"], "timestamp": "2025-11-06T12:30:00Z" } ``` ## Custom Metrics (Prometheus) ```python # Demo session metrics demo_sessions_created_total = Counter( 'demo_sessions_created_total', 'Total demo sessions created', ['scenario'] ) demo_sessions_active = Gauge( 'demo_sessions_active', 'Current active demo sessions', [] ) demo_session_duration_hours = Histogram( 'demo_session_duration_hours', 'Demo session duration', ['scenario'], buckets=[0.5, 1, 2, 4, 8, 12, 24, 48] ) demo_to_signup_conversions_total = Counter( 'demo_to_signup_conversions_total', 'Demo sessions that converted to signup', ['scenario'] ) demo_feature_usage_total = Counter( 'demo_feature_usage_total', 'Feature usage in demos', ['feature_name'] ) ``` ## Configuration ### Environment Variables **Service Configuration:** - `PORT` - Service port (default: 8019) - `DATABASE_URL` - Main PostgreSQL connection - `DEMO_DATABASE_URL` - Isolated demo database - `REDIS_URL` - Redis connection string - `RABBITMQ_URL` - RabbitMQ connection string **Demo Configuration:** - `DEFAULT_DEMO_DURATION_HOURS` - Default duration (default: 24) - `MAX_DEMO_DURATION_HOURS` - Maximum duration (default: 168/7 days) - `MAX_CONCURRENT_DEMOS` - Concurrent limit (default: 100) - `CLEANUP_INTERVAL_MINUTES` - Cleanup frequency (default: 60) **Data Seeding:** - `DEMO_SALES_HISTORY_DAYS` - Sales history length (default: 90) - `DEMO_PRODUCT_COUNT` - Number of products (default: 20) - `DEMO_SUPPLIER_COUNT` - Number of suppliers (default: 5) ## Development Setup ### Prerequisites - Python 3.11+ - PostgreSQL 17 (2 databases: main + demo) - Redis 7.4 - RabbitMQ 4.1 ### Local Development ```bash cd services/demo_session python -m venv venv source venv/bin/activate pip install -r requirements.txt export DATABASE_URL=postgresql://user:pass@localhost:5432/demo_session export DEMO_DATABASE_URL=postgresql://user:pass@localhost:5432/demo_data export REDIS_URL=redis://localhost:6379/0 export RABBITMQ_URL=amqp://guest:guest@localhost:5672/ alembic upgrade head python main.py ``` ## Integration Points ### Dependencies - **Separate Demo Database** - Isolated demo tenant data - **Auth Service** - Demo user credentials - **Data Generators** - Realistic data seeding - **PostgreSQL** - Session tracking - **Redis** - Rate limiting, caching - **RabbitMQ** - Event publishing ### Dependents - **Sales Team** - Demo creation - **Marketing** - Landing page demos - **Frontend** - Demo UI access - **Analytics** - Demo conversion tracking ## Business Value for VUE Madrid ### Problem Statement Traditional sales demos are difficult: - Time-consuming setup (15-30 minutes per demo) - Risk of breaking things in front of prospects - Inconsistent demo quality - No self-service for prospects - Hard to track engagement - Limited by sales rep availability ### Solution Bakery-IA Demo Session Service provides: - **Instant Demos**: Ready in 30 seconds - **Risk-Free**: Isolated environments - **Self-Service**: Prospects explore independently - **Consistent Quality**: Same data every time - **Engagement Tracking**: Know what prospects care about - **Scalable**: Unlimited concurrent demos ### Quantifiable Impact **Sales Efficiency:** - 30-50% shorter sales cycle with live demos - 2-3× conversion rate vs. static presentations - 5-10× more demos per sales rep - 0 minutes setup time vs. 15-30 minutes - €500-1,500/month sales time saved **Lead Quality:** - Higher engagement = more qualified leads - Feature usage indicates specific needs - Demo-to-trial conversion: 35-45% - Trial-to-paid conversion: 25-35% - Overall demo-to-paid: 12-16% **Marketing Value:** - Self-service demos on landing page - 24/7 availability for global prospects - Viral potential (shareable demo links) - Lower customer acquisition cost - Better understanding of product-market fit ### Target Market Fit (Spanish Bakeries) - **Visual Learners**: Spanish business culture values demonstrations - **Trust Building**: Try-before-buy reduces risk perception - **Language**: Demo data in Spanish increases resonance - **Realistic**: Spanish products, Madrid locations feel authentic ### ROI for Platform **Investment**: €100-300/month (compute + storage for demos) **Value Generated**: - 50+ demos/month → 20 trials → 6 paid customers - 6 customers × €66 avg MRR = €396/month - **Payback**: 1-3 months - **ROI**: 30-400% depending on conversion rates --- **Copyright © 2025 Bakery-IA. All rights reserved.**