Integrations
πŸ“˜ Integration Playbook

Integration Playbook

Complete Integration Guide: All integration patterns, setup guides, and best practices for connecting with the AltSportsLeagues.ai platform.

Integration Architecture Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   Integration Ecosystem                      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                               β”‚
β”‚  Frontend (altsportsleagues.ai)                              β”‚
β”‚      ↓ (HTTPS/WebSocket)                                     β”‚
β”‚  Backend API (api.altsportsleagues.ai/v1/*)                 β”‚
β”‚      ↓                                                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚
β”‚  β”‚  MCP Servers (Model Context Protocol)           β”‚        β”‚
β”‚  β”‚  β”œβ”€ n8n-mcp (workflow automation)               β”‚        β”‚
β”‚  β”‚  β”œβ”€ league-discovery (cross-comparison)         β”‚        β”‚
β”‚  β”‚  β”œβ”€ atlassian (Jira/Confluence)                 β”‚        β”‚
β”‚  β”‚  └─ google-workspace (Gmail/Calendar/Drive)     β”‚        β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
β”‚      ↓                                                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚
β”‚  β”‚  Data Layer (Unified Access)                    β”‚        β”‚
β”‚  β”‚  β”œβ”€ Neo4j (graph relationships)                 β”‚        β”‚
β”‚  β”‚  β”œβ”€ Supabase (relational data)                  β”‚        β”‚
β”‚  β”‚  β”œβ”€ Firebase (auth & real-time)                 β”‚        β”‚
β”‚  β”‚  └─ ChromaDB (embeddings)                       β”‚        β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
β”‚                                                               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

MCP Server Integration

Understanding MCP (Model Context Protocol)

MCP provides a standardized way to connect AI systems with external tools and data sources. The platform uses MCP servers for modular, scalable integrations.

Key Concepts:

  • Tools: Functions that AI can call (e.g., search leagues, create workflows)
  • Resources: Data sources that AI can access (e.g., league database, documentation)
  • Prompts: Pre-defined templates for common tasks

Setting Up MCP Servers

Configuration File (.cursor/mcp.json)

{
  "mcpServers": {
    "n8n-mcp": {
      "url": "https://altsportsdata.app.n8n.cloud",
      "apiKey": "${N8N_API_KEY}"
    },
    "league-discovery": {
      "command": "python",
      "args": ["-m", "apps.backend.mcp_servers.servers.league_discovery_cross_comparison_mcp"],
      "env": {
        "SUPABASE_URL": "${SUPABASE_URL}",
        "SUPABASE_KEY": "${SUPABASE_KEY}"
      }
    }
  }
}

Environment Variables Required

# n8n Integration
N8N_API_KEY=your_n8n_api_key
N8N_BASE_URL=https://altsportsdata.app.n8n.cloud
 
# Database Connections
SUPABASE_URL=https://vljfrdsqtmdujhoxwtig.supabase.co
SUPABASE_KEY=your_supabase_key
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=your_password
 
# Firebase
FIREBASE_PROJECT_ID=your_project_id
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
 
# Google Workspace
GMAIL_CLIENT_ID=your_client_id
GMAIL_CLIENT_SECRET=your_client_secret
GMAIL_REFRESH_TOKEN=your_refresh_token

Creating a Custom MCP Server

FastMCP Example (Python)

# apps/backend/mcp_servers/servers/custom_league_mcp.py
from fastmcp import FastMCP
from typing import Optional
 
mcp = FastMCP("custom-league-server")
 
@mcp.tool()
async def search_leagues(
    sport: str,
    region: Optional[str] = None,
    limit: int = 10
) -> list[dict]:
    """
    Search for sports leagues by sport type and region.
 
    Args:
        sport: Sport type (e.g., basketball, soccer)
        region: Geographic region filter
        limit: Maximum results to return
    """
    # Implementation using data_layer
    from data_layer.shared.neo4j_utils import query_neo4j
 
    cypher = """
    MATCH (l:League)-[:PLAYS]->(s:Sport {name: $sport})
    WHERE $region IS NULL OR l.region = $region
    RETURN l
    LIMIT $limit
    """
 
    results = await query_neo4j(cypher, {
        "sport": sport,
        "region": region,
        "limit": limit
    })
 
    return [dict(r["l"]) for r in results]
 
@mcp.resource("league://schema")
def get_league_schema() -> str:
    """Get the League data schema"""
    return """
    League Schema:
    - id: string (unique identifier)
    - name: string
    - sport: string
    - region: string
    - founded: number
    - teams_count: number
    """
 
if __name__ == "__main__":
    mcp.run()

TypeScript MCP Example

// .claude/mcp/custom-server/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 
const server = new Server({
  name: "custom-league-server",
  version: "1.0.0"
}, {
  capabilities: {
    tools: {},
    resources: {}
  }
});
 
// Define a tool
server.setRequestHandler("tools/call", async (request) => {
  if (request.params.name === "search_leagues") {
    const { sport, region, limit } = request.params.arguments;
 
    // Implementation
    const response = await fetch(
      `https://api.altsportsleagues.ai/v1/leagues?sport=${sport}&region=${region}&limit=${limit}`
    );
 
    return {
      content: [{
        type: "text",
        text: JSON.stringify(await response.json(), null, 2)
      }]
    };
  }
});
 
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);

Testing MCP Servers

# Test MCP server directly
python -m apps.backend.mcp_servers.servers.custom_league_mcp
 
# Test via Claude Code
# Use MCP tools in Claude Code with /mcp command
 
# Validate with n8n-mcp
# Use n8n_diagnostic tool to verify connectivity

n8n Workflow Integration

Workflow Architecture

n8n provides the automation backbone for league onboarding and data processing.

Workflow Structure:

orchestrator/parent-agent.json     # Main router
agents/email-agent.json            # Email processing
agents/calendar-agent.json         # Calendar management
agents/contact-agent.json          # Contact management
agents/content-creator-agent.json  # Content generation

Setting Up n8n Integration

1. Install n8n (Self-Hosted)

# Docker Compose
docker run -d \
  --name n8n \
  -p 5678:5678 \
  -e N8N_BASIC_AUTH_ACTIVE=true \
  -e N8N_BASIC_AUTH_USER=admin \
  -e N8N_BASIC_AUTH_PASSWORD=your_password \
  -v ~/.n8n:/home/node/.n8n \
  n8nio/n8n

2. Configure OAuth for Google Workspace

In n8n UI β†’ Settings β†’ Credentials:

  • Gmail: OAuth2 credentials
  • Google Calendar: OAuth2 credentials
  • Google Drive: OAuth2 credentials

Required scopes:

https://www.googleapis.com/auth/gmail.modify
https://www.googleapis.com/auth/calendar
https://www.googleapis.com/auth/drive.file

3. Create a League Onboarding Workflow

Example Workflow (Simplified):

{
  "name": "League Onboarding",
  "nodes": [
    {
      "name": "Gmail Trigger",
      "type": "n8n-nodes-base.gmailTrigger",
      "parameters": {
        "filters": {
          "subject": "New League Questionnaire"
        }
      }
    },
    {
      "name": "Extract Attachments",
      "type": "n8n-nodes-base.httpRequest",
      "parameters": {
        "url": "https://api.altsportsleagues.ai/v1/email/extract-attachments",
        "method": "POST",
        "body": "={{$json.attachments}}"
      }
    },
    {
      "name": "Process with AI",
      "type": "n8n-nodes-base.openAi",
      "parameters": {
        "operation": "completion",
        "prompt": "Extract league information from: {{$json.text}}"
      }
    },
    {
      "name": "Store in Supabase",
      "type": "n8n-nodes-base.supabase",
      "parameters": {
        "operation": "insert",
        "table": "leagues",
        "data": "={{$json}}"
      }
    },
    {
      "name": "Create Graph Node",
      "type": "n8n-nodes-base.httpRequest",
      "parameters": {
        "url": "https://api.altsportsleagues.ai/v1/neo4j/create-league",
        "method": "POST",
        "body": "={{$json}}"
      }
    },
    {
      "name": "Send Confirmation",
      "type": "n8n-nodes-base.gmail",
      "parameters": {
        "operation": "send",
        "to": "={{$json.email}}",
        "subject": "League Onboarding Complete",
        "message": "Your league has been successfully added!"
      }
    }
  ]
}

4. Upload Workflows via MCP

# Using n8n-mcp server
from fastmcp import FastMCP
 
mcp = FastMCP.connect("n8n-mcp")
 
# Create workflow
workflow = mcp.call_tool("n8n_create_workflow", {
    "name": "League Onboarding",
    "nodes": [...],
    "connections": {...}
})
 
# Activate workflow
mcp.call_tool("n8n_activate_workflow", {
    "id": workflow["id"]
})

Webhook Integration Pattern

// Frontend trigger
const response = await fetch('/api/v1/webhooks/n8n/league-onboarding', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    leagueData: formData,
    attachments: uploadedFiles
  })
});
 
// n8n receives webhook
// β†’ Processes data
// β†’ Calls backend API
// β†’ Updates databases
// β†’ Sends notifications

Backend API Integration

Authentication

All API calls require Firebase Authentication tokens.

// Get Firebase token
import { getAuth } from 'firebase/auth';
 
const auth = getAuth();
const user = auth.currentUser;
const token = await user.getIdToken();
 
// Make API call
const response = await fetch('https://api.altsportsleagues.ai/v1/leagues', {
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  }
});

API Client Pattern

// lib/api-client.ts
export class AltSportsAPIClient {
  private baseUrl: string;
  private token: string;
 
  constructor(token: string) {
    this.baseUrl = 'https://api.altsportsleagues.ai';
    this.token = token;
  }
 
  private async request(endpoint: string, options: RequestInit = {}) {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      ...options,
      headers: {
        'Authorization': `Bearer ${this.token}`,
        'Content-Type': 'application/json',
        ...options.headers
      }
    });
 
    if (!response.ok) {
      throw new Error(`API Error: ${response.statusText}`);
    }
 
    return response.json();
  }
 
  async getLeagues(params?: {
    sport?: string;
    region?: string;
    limit?: number;
  }) {
    const query = new URLSearchParams(params as any).toString();
    return this.request(`/v1/leagues?${query}`);
  }
 
  async getLeague(id: string) {
    return this.request(`/v1/leagues/${id}`);
  }
 
  async searchLeagues(query: string) {
    return this.request(`/v1/leagues/discovery?query=${query}`);
  }
}
 
// Usage
const client = new AltSportsAPIClient(token);
const leagues = await client.getLeagues({ sport: 'basketball' });

Error Handling Pattern

try {
  const leagues = await client.getLeagues();
} catch (error) {
  if (error instanceof APIError) {
    // Handle specific API errors
    switch (error.statusCode) {
      case 401:
        // Redirect to login
        router.push('/login');
        break;
      case 429:
        // Rate limited
        toast.error('Too many requests. Please try again later.');
        break;
      case 500:
        // Server error
        toast.error('Server error. Please try again.');
        break;
    }
  }
}

Firebase Integration

Authentication Setup

// lib/firebase.ts
import { initializeApp } from 'firebase/app';
import { getAuth, GoogleAuthProvider } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
 
const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID
};
 
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export const db = getFirestore(app);
export const googleProvider = new GoogleAuthProvider();

Authentication Flow

// components/LoginButton.tsx
import { signInWithPopup } from 'firebase/auth';
import { auth, googleProvider } from '@/lib/firebase';
 
export function LoginButton() {
  const handleLogin = async () => {
    try {
      const result = await signInWithPopup(auth, googleProvider);
      const token = await result.user.getIdToken();
 
      // Store token for API calls
      localStorage.setItem('authToken', token);
 
      // Redirect to dashboard
      router.push('/dashboard');
    } catch (error) {
      console.error('Login failed:', error);
    }
  };
 
  return <button onClick={handleLogin}>Sign in with Google</button>;
}

Real-time Data Sync

// hooks/useLeagueUpdates.ts
import { doc, onSnapshot } from 'firebase/firestore';
import { db } from '@/lib/firebase';
 
export function useLeagueUpdates(leagueId: string) {
  const [league, setLeague] = useState(null);
 
  useEffect(() => {
    const unsubscribe = onSnapshot(
      doc(db, 'leagues', leagueId),
      (doc) => {
        setLeague({ id: doc.id, ...doc.data() });
      }
    );
 
    return () => unsubscribe();
  }, [leagueId]);
 
  return league;
}

Database Integrations

Neo4j Graph Queries

# data_layer/shared/neo4j_utils.py
from neo4j import GraphDatabase
 
class Neo4jClient:
    def __init__(self, uri: str, user: str, password: str):
        self.driver = GraphDatabase.driver(uri, auth=(user, password))
 
    async def query(self, cypher: str, params: dict = None):
        with self.driver.session() as session:
            result = session.run(cypher, params or {})
            return [dict(record) for record in result]
 
    async def create_league(self, league_data: dict):
        cypher = """
        CREATE (l:League {
            id: $id,
            name: $name,
            sport: $sport,
            region: $region
        })
        RETURN l
        """
        return await self.query(cypher, league_data)
 
    async def find_related_leagues(self, league_id: str):
        cypher = """
        MATCH (l:League {id: $league_id})-[:PLAYS]->(s:Sport)
        MATCH (related:League)-[:PLAYS]->(s)
        WHERE related.id <> $league_id
        RETURN related
        """
        return await self.query(cypher, {"league_id": league_id})

Supabase Integration

// lib/supabase.ts
import { createClient } from '@supabase/supabase-js';
 
const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
 
// Query leagues
export async function getLeagues() {
  const { data, error } = await supabase
    .from('leagues')
    .select('*')
    .order('name', { ascending: true });
 
  if (error) throw error;
  return data;
}
 
// Insert league
export async function createLeague(league: LeagueInsert) {
  const { data, error } = await supabase
    .from('leagues')
    .insert(league)
    .select()
    .single();
 
  if (error) throw error;
  return data;
}
 
// Real-time subscription
export function subscribeToLeagueUpdates(
  leagueId: string,
  callback: (league: League) => void
) {
  return supabase
    .channel(`league:${leagueId}`)
    .on('postgres_changes', {
      event: '*',
      schema: 'public',
      table: 'leagues',
      filter: `id=eq.${leagueId}`
    }, (payload) => {
      callback(payload.new as League);
    })
    .subscribe();
}

Complete Integration Example: League Onboarding Flow

End-to-End Flow

1. User submits questionnaire (Frontend)
   ↓
2. Frontend calls: POST /api/v1/onboarding/submit
   ↓
3. Backend validates and triggers n8n webhook
   ↓
4. n8n workflow processes:
   - Extracts PDF attachments (if any)
   - Classifies intent with AI
   - Routes to appropriate agent
   ↓
5. Email agent processes:
   - Parses league information
   - Validates data quality
   - Calls backend API
   ↓
6. Backend stores data:
   - Supabase: Structured league data
   - Neo4j: Graph relationships
   - Firebase: Real-time updates
   ↓
7. Backend generates response:
   - Contract draft
   - Next steps
   - Calendar invite
   ↓
8. n8n sends notifications:
   - Email to league owner
   - Slack message to team
   - Calendar event created
   ↓
9. Frontend updates in real-time:
   - Dashboard shows new league
   - Graph visualization updates
   - User receives confirmation

Implementation Code

// Frontend: Submit onboarding form
async function handleSubmit(formData: LeagueOnboardingForm) {
  const token = await auth.currentUser.getIdToken();
 
  const response = await fetch('/api/v1/onboarding/submit', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(formData)
  });
 
  const result = await response.json();
 
  // Listen for real-time updates
  const unsubscribe = subscribeToLeagueUpdates(
    result.leagueId,
    (updatedLeague) => {
      setLeague(updatedLeague);
      toast.success('League updated!');
    }
  );
 
  return result;
}
# Backend: Process onboarding
from fastapi import APIRouter, Depends
from services.firebase_user_service import get_current_user
 
router = APIRouter(prefix="/v1/onboarding")
 
@router.post("/submit")
async def submit_onboarding(
    data: LeagueOnboardingData,
    user = Depends(get_current_user)
):
    # 1. Validate data
    validated = validate_league_data(data)
 
    # 2. Store in Supabase
    league = await supabase.from_("leagues").insert(validated).execute()
 
    # 3. Create graph node in Neo4j
    await neo4j.create_league_node(league.data[0])
 
    # 4. Trigger n8n workflow
    await trigger_n8n_workflow("league-onboarding", {
        "league_id": league.data[0]["id"],
        "user_email": user.email
    })
 
    # 5. Send real-time update via Firebase
    await firebase.update_document(
        f"leagues/{league.data[0]['id']}",
        {"status": "processing"}
    )
 
    return {
        "success": True,
        "leagueId": league.data[0]["id"],
        "message": "Onboarding started"
    }

Testing Integrations

Integration Test Suite

# tests/integration/test_onboarding_flow.py
import pytest
from httpx import AsyncClient
 
@pytest.mark.asyncio
async def test_complete_onboarding_flow(
    client: AsyncClient,
    firebase_token: str,
    test_league_data: dict
):
    # 1. Submit onboarding
    response = await client.post(
        "/v1/onboarding/submit",
        json=test_league_data,
        headers={"Authorization": f"Bearer {firebase_token}"}
    )
    assert response.status_code == 200
    league_id = response.json()["leagueId"]
 
    # 2. Verify Supabase storage
    supabase_data = await supabase.from_("leagues").select("*").eq("id", league_id).execute()
    assert len(supabase_data.data) == 1
 
    # 3. Verify Neo4j node
    neo4j_result = await neo4j.query(
        "MATCH (l:League {id: $id}) RETURN l",
        {"id": league_id}
    )
    assert len(neo4j_result) == 1
 
    # 4. Verify Firebase real-time update
    firebase_doc = await firebase.get_document(f"leagues/{league_id}")
    assert firebase_doc["status"] == "processing"
 
    # 5. Wait for n8n workflow completion
    await asyncio.sleep(5)
 
    # 6. Verify final state
    final_response = await client.get(f"/v1/leagues/{league_id}")
    assert final_response.json()["status"] == "active"

Troubleshooting

Common Issues

1. MCP Server Not Connecting

# Check MCP server status
python -m apps.backend.mcp_servers.servers.league_discovery_cross_comparison_mcp
 
# Verify environment variables
echo $SUPABASE_URL
echo $SUPABASE_KEY
 
# Test connection manually
curl -X POST http://localhost:8080/mcp/call-tool \
  -H "Content-Type: application/json" \
  -d '{"tool": "search_leagues", "args": {"sport": "basketball"}}'

2. n8n Webhook Not Triggering

  • Verify webhook URL is correct
  • Check n8n workflow is active
  • Verify OAuth credentials are valid
  • Check firewall/network settings

3. Firebase Auth Token Expired

// Auto-refresh token
auth.onIdTokenChanged(async (user) => {
  if (user) {
    const token = await user.getIdToken(true); // Force refresh
    localStorage.setItem('authToken', token);
  }
});

4. Database Connection Issues

# Test Neo4j connection
from neo4j import GraphDatabase
 
driver = GraphDatabase.driver(
    os.getenv("NEO4J_URI"),
    auth=(os.getenv("NEO4J_USER"), os.getenv("NEO4J_PASSWORD"))
)
 
with driver.session() as session:
    result = session.run("RETURN 1 as test")
    print(result.single()["test"])  # Should print: 1

Best Practices

  1. Use Environment Variables: Never hardcode credentials
  2. Implement Retry Logic: Networks fail, handle gracefully
  3. Monitor Webhook Health: Set up alerts for failed webhooks
  4. Version Your APIs: Use /v1/ prefix for all endpoints
  5. Document Integration Points: Keep integration docs up-to-date
  6. Test End-to-End: Integration tests catch real-world issues
  7. Use Type Safety: TypeScript/Pydantic for all integrations
  8. Implement Rate Limiting: Protect against abuse
  9. Log Everything: Comprehensive logging aids debugging
  10. Cache When Possible: Reduce API calls with intelligent caching

Resources


Last Updated: November 2024 Version: 2.1.0

Platform

Documentation

Community

Support

partnership@altsportsdata.comdev@altsportsleagues.ai

2025 Β© AltSportsLeagues.ai. Powered by AI-driven sports business intelligence.

πŸ€– AI-Enhancedβ€’πŸ“Š Data-Drivenβ€’βš‘ Real-Time