{"openapi":"3.1.0","info":{"title":"Bitcast API","description":"\n    Scalable API system for database access with connection pooling.\n    \n    ## Authentication\n    This API requires authentication via either:\n    - **IP Whitelist**: Your IP must be whitelisted in nginx\n    - **API Key**: Pass `X-API-Key` header with a valid key\n    \n    ## V2 API (Recommended)\n    Multi-platform architecture with explicit platform namespaces:\n    - `/api/v2/youtube/*` - YouTube-specific endpoints\n    - `/api/v2/twitter/*` - Twitter/X-specific endpoints\n\n    \n    ## V1 API (Legacy - Being Sunset)\n    Original API structure without platform namespaces.\n    Please migrate to v2 for clearer, more maintainable code.\n    ","version":"2.0.0"},"paths":{"/health":{"get":{"tags":["health"],"summary":"Health Check","description":"Liveness health check — returns 200 as long as the process is running.\nNo DB check here so the container passes health checks immediately on startup.\nUsed by the ECS container health check (with startPeriod).","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthCheckResponse"}}}}}}},"/health/ready":{"get":{"tags":["health"],"summary":"Readiness Check","description":"Readiness check — verifies DB connectivity.\nUsed by the ELB target group to determine if traffic should be routed.\nReturns 200 if DB is reachable, 503 otherwise.","operationId":"readiness_check_health_ready_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health/database":{"get":{"tags":["health"],"summary":"Database Health Check","description":"Detailed database health check with connection pool status.\n\nProvides comprehensive information about database connectivity and\nconnection pool metrics including active connections, pool size, and overflow.\n\n**Returns:**\n```json\n{\n    \"database\": {\n        \"healthy\": true,\n        \"message\": \"Database connection successful\"\n    },\n    \"connection_pool\": {\n        \"pool_size\": 10,\n        \"checked_in\": 8,\n        \"checked_out\": 2,\n        \"overflow\": 0,\n        \"max_overflow\": 20\n    },\n    \"timestamp\": 1642682400.0\n}\n```\n\n**Status Codes:**\n- **200**: Database is healthy\n- **503**: Database is unhealthy (connection issues)","operationId":"database_health_check_health_database_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DatabaseHealthResponse"}}}}}}},"/":{"get":{"tags":["root"],"summary":"Root","description":"Root endpoint with basic API information.\n\nRequires either IP whitelist or valid API key authentication.","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/briefs":{"get":{"tags":["v1-briefs (LEGACY - migrate to /api/v2/youtube/briefs)"],"summary":"Get Briefs","description":"Retrieve briefs with user-specific filtering.\n\n**User Filtering:**\n- New users (< 6 weeks): See all briefs including intro_* briefs\n- Old users (>= 6 weeks): See only non-intro briefs\n- No user ID: See only non-intro briefs (safe default)\n\n**Caching:**\n- Brief data cached in 2 sets (4-hour TTL)\n- User classification cached per-user (72-hour TTL)\n- Responses assembled on-the-fly (no per-user response cache)\n\n**Parameters:**\n- **portal_user_id**: Optional portal user ID\n\n**Returns:**\n- List of briefs appropriate for user's age\n- Total count of briefs returned\n\n**Response Format:**\n```json\n{\n    \"briefs\": [\n        {\n            \"id_brief\": 1,\n            \"brief_id\": \"brief_001\", \n            \"brief\": \"Brief content description\",\n            \"start_date\": \"2025-01-01\",\n            \"end_date\": \"2025-01-31\",\n            \"format\": \"adRead\",\n            \"boost\": 1.5,\n            \"cap\": 100.0,\n            \"prompt_version\": 1,\n            \"unique_identifier\": null,\n            \"brand\": 1,\n            \"budget\": 1000.0,\n            \"budget_multiplier\": 1.5,\n            \"max_count\": 100,\n            \"status\": \"Live\"\n        }\n    ],\n    \"total_count\": 1\n}\n```\n\n**Errors:**\n- **500**: Database error occurred","operationId":"get_briefs_api_v1_briefs_get","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Portal user ID","title":"Portal User Id"},"description":"Portal user ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefsListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/briefs/health":{"get":{"tags":["v1-briefs (LEGACY - migrate to /api/v2/youtube/briefs)","health"],"summary":"Briefs Service Health","description":"Health check endpoint for the briefs service.\n\nTests database connectivity specifically for brief operations.\n\n**Returns:**\n```json\n{\n    \"status\": \"healthy\",\n    \"service\": \"briefs\",\n    \"database_accessible\": true,\n    \"timestamp\": 1642682400.0\n}\n```\n\n**Status Codes:**\n- **200**: Service is healthy\n- **503**: Service is unhealthy (database issues)","operationId":"briefs_service_health_api_v1_briefs_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceHealthResponse"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/briefs/aggregated":{"get":{"tags":["v1-briefs (LEGACY - migrate to /api/v2/youtube/briefs)"],"summary":"Get Aggregated Brief Stats","description":"Retrieve aggregated key statistics across ALL briefs.\n\nThis endpoint provides comprehensive aggregated statistics including:\n- Total views across all videos from all briefs\n- Total minutes watched across all videos from all briefs\n- Total video count across all briefs\n- Total brief count\n- Cumulative timeseries data (views and minutes by date)\n- Latest data timestamp across all briefs\n\n**Caching:**\n- Response is cached for 18 hours (64,800 seconds)\n- Single cache for all requests (no parameters)\n\n**Note:** This endpoint does NOT include brief-specific information, \nonly aggregated statistics across the entire system.\n\n**Returns:**\nAggregated statistics and cumulative timeseries data across all briefs\n\n**Response Format:**\n```json\n{\n    \"aggregated_stats\": {\n        \"total_views\": 2500000,\n        \"total_minutes_watched\": 125000,\n        \"video_count\": 45,\n        \"brief_count\": 5,\n        \"latest_data_timestamp\": \"2025-01-28T10:30:00Z\"\n    },\n    \"timeseries_data\": {\n        \"views\": {\n            \"2025-01-15\": 25000,\n            \"2025-01-16\": 55000,\n            \"2025-01-17\": 75000\n        },\n        \"minutes_watched\": {\n            \"2025-01-15\": 1250,\n            \"2025-01-16\": 2750,\n            \"2025-01-17\": 3750\n        }\n    }\n}\n```\n\n**Data Strategy:**\n- Aggregates data from ALL briefs in the system\n- For each video, uses most recent timeseries data available\n- Aggregates data across all matched videos from all briefs\n- Returns all available historical data (no date range limits)\n- Timeseries data is cumulative (each date shows running total, not daily values)\n- Includes count of briefs contributing to the aggregation\n- Uses hardcoded static data for minutes watched before 2025-08-13 (converted from hours to minutes)\n\n**Performance:**\n- Uses optimized queries with proper indexes\n- Includes 18-hour response caching for optimal performance\n- Handles large datasets efficiently with single aggregation query\n\n**Error Responses:**\n- **200 with zero values**: No briefs or videos exist (valid response)\n- **500**: Internal server error or database connectivity issues","operationId":"get_aggregated_brief_stats_api_v1_briefs_aggregated_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AggregatedBriefResponse"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/briefs/{brief_id}":{"get":{"tags":["v1-briefs (LEGACY - migrate to /api/v2/youtube/briefs)"],"summary":"Get Brief Details","description":"Retrieve detailed information for a specific brief including aggregated statistics and cumulative timeseries data.\n\nThis endpoint provides comprehensive information about a brief including:\n- All basic brief information (same as /briefs endpoint)\n- Aggregated statistics across all videos ever matched to this brief\n- Cumulative historical timeseries data for views and minutes watched\n\n**Caching:**\n- Response is cached for 12 hours (43,200 seconds)\n- Cache key includes brief_id parameter\n\n**Parameters:**\n- **brief_id**: Brief ID in string format (e.g., \"brief_001\")\n\n**Returns:**\nDetailed brief information with aggregated stats and cumulative timeseries data\n\n**Response Format:**\n```json\n{\n    \"brief\": {\n        \"id_brief\": 1,\n        \"brief_id\": \"brief_001\", \n        \"brief\": \"Brief content description\",\n        \"start_date\": \"2025-01-01\",\n        \"end_date\": \"2025-01-31\",\n        \"format\": \"adRead\",\n        \"boost\": 1.5,\n        \"cap\": 100.0,\n        \"prompt_version\": 1,\n        \"unique_identifier\": null,\n        \"brand\": 1,\n        \"budget\": 1000.0,\n        \"budget_multiplier\": 1.5,\n        \"max_count\": 100,\n        \"status\": \"Live\"\n    },\n    \"aggregated_stats\": {\n        \"total_views\": 1500000,\n        \"total_minutes_watched\": 75000,\n        \"video_count\": 25,\n        \"latest_data_timestamp\": \"2025-01-28T10:30:00Z\"\n    },\n    \"timeseries_data\": {\n        \"views\": {\n            \"2025-01-15\": 10000,\n            \"2025-01-16\": 22000,\n            \"2025-01-17\": 30000\n        },\n        \"minutes_watched\": {\n            \"2025-01-15\": 500,\n            \"2025-01-16\": 1100,\n            \"2025-01-17\": 1500\n        }\n    }\n}\n```\n\n**Data Strategy:**\n- Includes ALL videos historically matched to this brief (not just current/active)\n- For each video, uses most recent timeseries data available\n- Aggregates data across all matched videos\n- Returns all available historical data (no date range limits)\n- Timeseries data is cumulative (each date shows running total, not daily values)\n\n**Error Responses:**\n- **404**: Brief ID not found\n- **200 with empty data**: Brief exists but no videos matched (per BA requirements)\n- **500**: Internal server error or database connectivity issues","operationId":"get_brief_details_api_v1_briefs__brief_id__get","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","description":"Brief ID (string format like 'brief_001') to retrieve details for","title":"Brief Id"},"description":"Brief ID (string format like 'brief_001') to retrieve details for"},{"name":"portal_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Portal user ID for template variable interpolation","title":"Portal User Id"},"description":"Portal user ID for template variable interpolation"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/briefs/{brief_id}/daily-views":{"get":{"tags":["v1-briefs (LEGACY - migrate to /api/v2/youtube/briefs)"],"summary":"Get Brief Daily Views","description":"Retrieve daily view counts broken down by creator (YouTube channel) for a specific brief.\n\nReturns timeseries view data aggregated by creator and date across all videos\nmatched to the brief. Covers the full lifetime of all matched videos.\n\n**Caching:**\n- Response is cached for 1 hour (3,600 seconds)\n\n**Response Format:**\n```json\n{\n    \"brief_id\": \"brief_001\",\n    \"creators\": [\"Creator A\", \"Creator B\"],\n    \"daily_data\": [\n        {\"date\": \"2025-01-15\", \"Creator A\": 5000, \"Creator B\": 3000},\n        {\"date\": \"2025-01-16\", \"Creator A\": 6000, \"Creator B\": 4000}\n    ]\n}\n```\n\nDesigned for direct consumption by Recharts stacked BarChart.","operationId":"get_brief_daily_views_api_v1_briefs__brief_id__daily_views_get","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","description":"Brief ID (string format like 'brief_001')","title":"Brief Id"},"description":"Brief ID (string format like 'brief_001')"},{"name":"identifiers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Creator identifier: 'channel_name' (default) or 'bitcast_id'","title":"Identifiers"},"description":"Creator identifier: 'channel_name' (default) or 'bitcast_id'"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__schemas__brief__BriefDailyViewsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/briefs/{brief_id}/geographic":{"get":{"tags":["v1-briefs (LEGACY - migrate to /api/v2/youtube/briefs)"],"summary":"Get Brief Geographic","description":"Retrieve geographic watch time data for a specific brief.\n\nReturns watch time by country for all videos matched to the given brief.\nUses the same format as the global geographic endpoint.\n\n**Caching:**\n- Response is cached for 1 hour (3,600 seconds)","operationId":"get_brief_geographic_api_v1_briefs__brief_id__geographic_get","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","description":"Brief ID (string format like 'brief_001')","title":"Brief Id"},"description":"Brief ID (string format like 'brief_001')"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Maximum number of countries to return (0 = all)","default":0,"title":"Limit"},"description":"Maximum number of countries to return (0 = all)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/briefs/{brief_id}/videos":{"get":{"tags":["v1-briefs (LEGACY - migrate to /api/v2/youtube/briefs)"],"summary":"Get Brief Videos","description":"Retrieve all videos matched to a specific brief.\n\nThis endpoint returns comprehensive information about all videos matched to a brief including:\n- Complete brief information (all fields from /briefs endpoint)\n- Video details: bitcast_video_id, title, published_at, duration, url\n- Account information: bitcast_account_id, account_title\n- Engagement metrics: views, likes, shares (from latest yt_video_run_metrics)\n- Brief budget information with CPM-based spend estimation:\n    - creator_budget: Brief budget amount allocated for creators\n    - budget_multiplier: Controls rate of budget drawdown (defaults to 1 if null)\n    - remaining_budget: creator_budget - (total_spend / budget_multiplier)\n    - usd_spend: Total USD spent (creator_budget - remaining_budget)\n\n**Caching:**\n- Response is cached for 12 hours (43,200 seconds)\n- Cache key includes brief_id parameter\n\n**Path Parameters:**\n- **brief_id**: Brief ID in string format (e.g., \"brief_001\")\n\n**Returns:**\nComprehensive brief and video information\n\n**Response Format:**\n```json\n{\n    \"brief_info\": {\n        \"id_brief\": 1,\n        \"brief_id\": \"brief_001\",\n        \"brief\": \"Brief content description\",\n        \"start_date\": \"2025-01-01\",\n        \"end_date\": \"2025-01-31\",\n        \"format\": \"adRead\",\n        \"boost\": 1.5,\n        \"cap\": 100.0,\n        \"prompt_version\": 1,\n        \"unique_identifier\": null,\n        \"brand\": 1,\n        \"max_count\": 100,\n        \"status\": \"Live\",\n        \"creator_budget\": 1000.0,\n        \"budget_multiplier\": 1.2,\n        \"remaining_budget\": 916.67,\n        \"usd_spend\": 83.33\n    },\n    \"videos\": [\n        {\n            \"bitcast_video_id\": \"vid_abc123\",\n            \"title\": \"Sample Video\",\n            \"published_at\": \"2025-01-15T10:30:00Z\",\n            \"duration\": \"PT10M30S\",\n            \"url\": \"https://youtube.com/watch?v=abc123\",\n            \"bitcast_account_id\": \"acc_xyz789\",\n            \"account_title\": \"Creator Channel Name\",\n            \"views\": 125000,\n            \"likes\": 3500,\n            \"shares\": 450\n        }\n    ],\n    \"total_count\": 1\n}\n```\n\n**Budget Calculation (CPM-Based):**\nThe spend estimation uses a creator-specific CPM approach:\n\n1. **Creator ARRPV (Average Red Revenue Per View):**\n   - For each creator, calculate: ARRPV = total_red_revenue / total_views (past 3 months)\n   - Uses actual YouTube red revenue data from yt_video_run_metrics\n   - Falls back to default ARRPV ($0.000398/view) for creators without 3-month history\n\n2. **Per-Video Estimation:**\n   - ERR (Estimated Red Revenue) = ARRPV × video_views\n   - estimated_spend = (400 × √ERR) / (1 + 0.1 × √ERR)\n\n3. **Total Spend Calculation:**\n   - total_spend = sum of all video estimated_spend values\n   - remaining_budget = creator_budget - (total_spend / budget_multiplier)\n   - usd_spend = creator_budget - remaining_budget\n\n4. **Budget Multiplier:**\n   - Controls rate of budget drawdown\n   - If budget_multiplier is 1.2, budget draws down 20% slower than actual spend\n   - Example: creator_budget=1000, multiplier=1.2, spend=100 → \n     remaining = 1000 - (100/1.2) = 916.67, usd_spend = 83.33\n   - Defaults to 1.0 if null in database\n\n5. **Edge Cases:**\n   - Videos with 0 views: estimated_spend = $0\n   - Creators without history: use default ARRPV = $0.000398/view\n   - Negative red revenue: treated as $0\n\n**Data Strategy:**\n- Returns ALL videos historically matched to this brief\n- Ordered by published_at descending (most recent first)\n- Includes videos regardless of current brief status (Live/Complete/Upcoming)\n- Uses metrics from the validator run when the video matched the brief\n\n**Error Responses:**\n- **404**: Brief ID not found\n- **500**: Internal server error or database connectivity issues","operationId":"get_brief_videos_api_v1_briefs__brief_id__videos_get","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","description":"Brief ID to get videos for","title":"Brief Id"},"description":"Brief ID to get videos for"},{"name":"portal_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Portal user ID for template variable interpolation","title":"Portal User Id"},"description":"Portal user ID for template variable interpolation"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefVideosResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/health":{"get":{"tags":["v1-users (LEGACY - migrate to /api/v2/youtube/users)","health"],"summary":"Users Service Health","description":"Health check endpoint for the users service.\n\nTests database connectivity specifically for user operations.\n\n**Returns:**\n```json\n{\n    \"status\": \"healthy\",\n    \"service\": \"users\",\n    \"database_accessible\": true,\n    \"timestamp\": 1642682400.0\n}\n```\n\n**Status Codes:**\n- **200**: Service is healthy\n- **503**: Service is unhealthy (database issues)","operationId":"users_service_health_api_v1_users_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceHealthResponse"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/users":{"get":{"tags":["v1-users (LEGACY - migrate to /api/v2/youtube/users)"],"summary":"Get User Details","description":"Retrieve user details by email or portal_user_id.\n\nJWT users can only access their own data. API keys and admins can query any user.","operationId":"get_user_details_api_v1_users_get","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"email","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"}},{"name":"portal_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Portal User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetailsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["v1-users (LEGACY - migrate to /api/v2/youtube/users)"],"summary":"Create User","description":"Create a new user.\n\nCreates a new user with the provided email address. All new users have\nmining_coldkey set to \"no-code\". A unique referral code is automatically\ngenerated from the user's email.\n\n**Parameters:**\n- **email**: The email address for the new user\n\n**Returns:**\n- Complete user data including empty YouTube credentials list and referral code\n\n**Request Format:**\n```json\n{\n    \"email\": \"user@example.com\"\n}\n```\n\n**Response Format:**\n```json\n{\n    \"id_portal_user\": 1,\n    \"email\": \"user@example.com\",\n    \"mining_coldkey\": \"no-code\",\n    \"payment_coldkey\": \"\",\n    \"referral_code\": \"f6g7h8i9j0\",\n    \"created_at\": \"2025-01-29T10:30:00Z\",\n    \"updated_at\": \"2025-01-29T10:30:00Z\",\n    \"yt_credentials\": []\n}\n```\n\n**Business Logic:**\n- mining_coldkey is always set to \"no-code\" for new users\n- payment_coldkey is set to empty string\n- platform_margin defaults to 0.0500 (5% margin)\n- referral_code is automatically generated from email (first 10 chars of SHA256 hash)\n- yt_credentials list is empty for new users\n- Referral tracking is now handled via the waitlist-details endpoint\n\n**Errors:**\n- **409**: User already exists with the provided email\n- **400**: Invalid request data (invalid email format)\n- **500**: Database error occurred","operationId":"create_user_api_v1_users_post","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/list":{"get":{"tags":["v1-users (LEGACY - migrate to /api/v2/youtube/users)"],"summary":"Get All Users","description":"Retrieve the full list of all users with ID and email.\n\n**Returns:**\nList of all users showing only portal_user_id and email for each user.\n\n**Response Format:**\n```json\n{\n    \"users\": [\n        {\n            \"id_portal_user\": 1,\n            \"email\": \"user1@example.com\"\n        },\n        {\n            \"id_portal_user\": 2, \n            \"email\": \"user2@example.com\"\n        }\n    ],\n    \"total_count\": 2\n}\n```\n\n**Use Case:**\nThis endpoint is useful for Q2.1 scenarios where you need to:\n- Get a user's ID via their email to fetch videos\n- Browse all available users in the system\n- Get a complete user directory for administrative purposes\n\n**Performance:** \nOptimized to return only ID and email fields for faster response times.","operationId":"get_all_users_api_v1_users_list_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserListResponse"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/users/{portal_user_id}/payment-coldkey":{"put":{"tags":["v1-users (LEGACY - migrate to /api/v2/youtube/users)"],"summary":"Update Payment Coldkey","description":"Add or update the payment coldkey for a user.\n\n**Parameters:**\n- **portal_user_id**: Portal user ID to update\n- **payment_coldkey**: Payment coldkey to set","operationId":"update_payment_coldkey_api_v1_users__portal_user_id__payment_coldkey_put","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","title":"Portal User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePaymentColdkeyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePaymentColdkeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/{portal_user_id}/waitlist-details":{"post":{"tags":["v1-users (LEGACY - migrate to /api/v2/youtube/users)"],"summary":"Create User Waitlist Details","description":"Create or update waitlist details for a user.\n\nStores social media URLs/handles (YouTube and X/Twitter) and referral information \nfor users on the waitlist. Uses UPSERT behavior - creates a new record if none exists, \nor updates the existing record.\n\n**Path Parameters:**\n- **portal_user_id**: The portal user ID (must be positive integer)\n\n**Request Body:**\n- **youtube**: YouTube URL or handle (optional, max 255 characters)\n    - Accepts full URLs: https://www.youtube.com/@channel, https://youtube.com/@channel\n    - Accepts simple handles: @channel\n- **x**: X/Twitter URL or handle (optional, max 255 characters)\n    - Accepts full URLs: https://x.com/handle, https://www.x.com/handle\n    - Accepts simple handles: @handle\n- **referred_by**: Referral code of the user who referred this user (optional, max 10 characters)\n\n**Validation:**\n- All fields are optional (can provide none, one, or multiple)\n- YouTube URLs must match pattern: (https?://)?(www\\.)?(youtube\\.com|youtu\\.be)/.+\n- X URLs must match pattern: (https?://)?(www\\.)?x\\.com/.+\n- Simple handles allow only: alphanumeric, @, _, -, and spaces\n- referred_by is not validated against existing referral codes (stored as-is)\n\n**Example Request:**\n```json\n{\n    \"youtube\": \"https://www.youtube.com/@example_channel\",\n    \"x\": \"https://x.com/example_handle\",\n    \"referred_by\": \"a1b2c3d4e5\"\n}\n```\n\n**Example Response (201 Created):**\n```json\n{\n    \"id_user_waitlist_details\": 1,\n    \"portal_user_id\": 1,\n    \"youtube\": \"https://www.youtube.com/@example_channel\",\n    \"x\": \"https://x.com/example_handle\",\n    \"created_at\": \"2025-10-10T10:00:00Z\",\n    \"updated_at\": \"2025-10-10T10:00:00Z\"\n}\n```\n\n**Status Codes:**\n- **201**: Waitlist details created or updated successfully\n- **422**: Invalid request data (Pydantic validation failed)\n- **404**: User not found (foreign key constraint violation)\n- **500**: Database error occurred\n\n**Notes:**\n- If waitlist details already exist for this user, they will be updated (UPSERT)\n- User must exist in portal_user table (enforced by foreign key)\n- Empty/whitespace-only URLs/handles are treated as null\n- This endpoint now handles referral tracking (updates portal_user.referred_by)","operationId":"create_user_waitlist_details_api_v1_users__portal_user_id__waitlist_details_post","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","minimum":1,"description":"Portal user ID","title":"Portal User Id"},"description":"Portal user ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWaitlistDetailsRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaitlistDetailsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/{portal_user_id}/notification-preferences":{"patch":{"tags":["v1-users (LEGACY - migrate to /api/v2/youtube/users)"],"summary":"Update Notification Preferences","description":"Update notification preferences for a user.\n\nSupports partial updates - you can update just email, just telegram, or both.\nUses PATCH method for partial resource updates (REST best practice).\n\n**Parameters:**\n- **portal_user_id**: Portal user ID to update (path parameter)\n- **notify_email**: Email notification preference (optional, boolean)\n- **notify_telegram**: Telegram notification preference (optional, boolean)\n\n**Example Request (update both):**\n```json\n{\n    \"notify_email\": true,\n    \"notify_telegram\": false\n}\n```\n\n**Example Request (update only email):**\n```json\n{\n    \"notify_email\": false\n}\n```\n\n**Example Response:**\n```json\n{\n    \"id_portal_user\": 1,\n    \"notify_email\": false,\n    \"notify_telegram\": true\n}\n```\n\n**Status Codes:**\n- **200**: Notification preferences updated successfully\n- **400**: Invalid request (no fields provided or invalid data)\n- **404**: User not found\n- **500**: Database error occurred","operationId":"update_notification_preferences_api_v1_users__portal_user_id__notification_preferences_patch","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","minimum":1,"description":"Portal user ID","title":"Portal User Id"},"description":"Portal user ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateNotificationPreferencesRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/{portal_user_id}/telegram-chat-id":{"put":{"tags":["v1-users (LEGACY - migrate to /api/v2/youtube/users)"],"summary":"Update Telegram Chat Id","description":"Update the Telegram chat ID for a user.\n\nThis endpoint allows updating or clearing the Telegram chat ID associated with a user account.\nThe Telegram chat ID is used for sending notifications via Telegram.\n\n**Path Parameters:**\n- **portal_user_id**: The portal user ID (must be positive integer)\n\n**Request Body:**\n- **telegram_chat_id**: Telegram chat ID to set (optional, null to clear)\n\n**Example Request (set chat ID):**\n```json\n{\n    \"telegram_chat_id\": \"123456789\"\n}\n```\n\n**Example Request (clear chat ID):**\n```json\n{\n    \"telegram_chat_id\": null\n}\n```\n\n**Example Response:**\n```json\n{\n    \"id_portal_user\": 1,\n    \"telegram_chat_id\": \"123456789\",\n    \"updated_at\": \"2025-11-18T10:30:00Z\"\n}\n```\n\n**Status Codes:**\n- **200**: Telegram chat ID updated successfully\n- **404**: User not found\n- **500**: Database error occurred\n\n**Notes:**\n- Empty strings are treated as null (chat ID is cleared)\n- Whitespace is automatically trimmed from the chat ID\n- User must exist in portal_user table","operationId":"update_telegram_chat_id_api_v1_users__portal_user_id__telegram_chat_id_put","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","minimum":1,"description":"Portal user ID","title":"Portal User Id"},"description":"Portal user ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTelegramChatIdRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTelegramChatIdResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/resolve-wallet":{"get":{"tags":["v1-users (LEGACY - migrate to /api/v2/youtube/users)"],"summary":"Resolve Wallet","description":"Public endpoint to resolve a wallet address to creator accounts.\n\nLooks up both YouTube and X/Twitter accounts associated with the given wallet.\nCase-insensitive matching is used. Returns all matching creators.\n\n**Query Parameters:**\n- **address**: Wallet address to look up (required)\n- **walletType**: Optional filter for 'evm' or 'bittensor' (accepted but not used yet)\n\n**Returns:**\nList of matching creators with username, display name, and abbreviated wallet for confirmation.\n\n**Example Request:**\n```\nGET /users/resolve-wallet?address=0x1234567890abcdef1234567890abcdef12345678\n```\n\n**Example Response:**\n```json\n{\n    \"success\": true,\n    \"data\": [\n        {\n            \"username\": \"@channel\",\n            \"display_name\": \"Channel Name\",\n            \"payment_wallet_abbr\": \"0x12345\",\n            \"platform\": \"x\"\n        },\n        {\n            \"username\": \"@channel2\",\n            \"display_name\": \"Channel 2\",\n            \"payment_wallet_abbr\": \"0x12345\",\n            \"platform\": \"youtube\"\n        }\n    ]\n}\n```\n\n**Status Codes:**\n- **200**: Success (returns empty list if no matches)\n- **400**: Invalid address\n- **500\": Server error","operationId":"resolve_wallet_api_v1_users_resolve_wallet_get","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"address","in":"query","required":true,"schema":{"type":"string","description":"Wallet address to look up","title":"Address"},"description":"Wallet address to look up"},{"name":"walletType","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"'evm' or 'bittensor'","title":"Wallettype"},"description":"'evm' or 'bittensor'"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveWalletResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/registration-status":{"get":{"tags":["v1-users (LEGACY - migrate to /api/v2/youtube/users)"],"summary":"Get Registration Status","description":"Get registration status and verification tag for a wallet address.\nReturns the verification tag needed for the registration tweet.","operationId":"get_registration_status_api_v1_users_registration_status_get","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"address","in":"query","required":true,"schema":{"type":"string","description":"Wallet address to check","title":"Address"},"description":"Wallet address to check"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/credentials/upload":{"post":{"tags":["v1-credentials (LEGACY - migrate to /api/v2/youtube/credentials)"],"summary":"Upload YouTube JSON credential file","description":"Upload and validate YouTube API credentials in JSON format. JSON files are automatically converted to Google OAuth2 Credentials objects using system-provided client credentials.","operationId":"upload_credentials_api_v1_credentials_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_credentials_api_v1_credentials_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialUploadResponse"}}}},"400":{"description":"Invalid file or validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialUploadError"}}}},"404":{"description":"Portal user not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialUploadError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialUploadError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/credentials/health":{"get":{"tags":["v1-credentials (LEGACY - migrate to /api/v2/youtube/credentials)"],"summary":"Credential system health check","description":"Check if the credential management system is operational","operationId":"credential_system_health_api_v1_credentials_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialHealthResponse"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/users/{portal_user_id}/videos/{bitcast_video_id}":{"get":{"tags":["v1-videos (LEGACY - migrate to /api/v2/youtube/videos)"],"summary":"Get Single Video","description":"Retrieve a single video with detailed calculated metrics.\n\nThis endpoint returns comprehensive details for a single video including \ncalculated earnings metrics, daily time-series data, and additional metadata.\n\n**Path Parameters:**\n- **portal_user_id**: Portal user ID (currently not used for authorization but required for API consistency)\n- **bitcast_video_id**: Unique Bitcast video identifier\n\n**Returns:**\nSingle video with complete calculated metrics and daily time-series data\n\n**Calculated Fields:**\n- **brief_id**: Brief ID if video is matched to a brief, null otherwise (handles multiple brief matches by selecting lowest brief ID)\n- **boost**: Brief boost multiplier from matched brief, null if no brief matched\n- **format**: Brief format (e.g., 'adRead', 'dedicated') from matched brief, null if no brief matched\n- **daily_earnings_usd**: Average usd_target from validator runs in past 24 hours (with weight corrections)\n- **daily_earnings_alpha**: Average alpha_target from validator runs in past 24 hours (with weight corrections)\n- **usd_total**: Sum of daily averages for usd_target across entire video history (with weight corrections)\n- **alpha_total**: Sum of daily averages for alpha_target across entire video history (with weight corrections)\n- **daily_metrics**: Time-series array of daily earnings by date\n- **views**: Latest view count from video run metrics\n- **estimated_minutes_watched**: Latest watch time estimate from video run metrics\n- **estimated_red_partner_revenue**: Latest partner revenue estimate from video run metrics\n- **status**: Video monetization status:\n    - \"Earning\": Video matched to brief and published ≤ 3 weeks ago\n    - \"Complete\": Video matched to brief and published > 3 weeks ago  \n    - \"Non-monotized\": Video not matched to any brief\n\n**Response Format:**\n```json\n{\n    \"title\": \"Sample Video Title\",\n    \"views\": 5000,\n    \"published_at\": \"2025-01-15T10:30:00Z\",\n    \"daily_earnings_usd\": 25.50,\n    \"daily_earnings_alpha\": 200.75,\n    \"usd_total\": 150.75,\n    \"alpha_total\": 1250.25,\n    \"daily_metrics\": [\n        {\n            \"date\": \"2025-01-15\",\n            \"total_usd\": 10.50,\n            \"total_alpha\": 100.25\n        },\n        {\n            \"date\": \"2025-01-16\", \n            \"total_usd\": 15.25,\n            \"total_alpha\": 125.50\n        }\n    ],\n    \"brief_id\": \"brief_001\",\n    \"boost\": 1.5,\n    \"format\": \"adRead\",\n    \"estimated_minutes_watched\": 15000,\n    \"estimated_red_partner_revenue\": 25.50,\n    \"status\": \"Earning\"\n}\n```\n\n**Errors:**\n- **404**: Video with specified bitcast_video_id not found\n- **500**: Database error occurred","operationId":"get_single_video_api_v1_users__portal_user_id__videos__bitcast_video_id__get","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","description":"Portal user ID (currently not used for authorization).","title":"Portal User Id"},"description":"Portal user ID (currently not used for authorization)."},{"name":"bitcast_video_id","in":"path","required":true,"schema":{"type":"string","description":"The Bitcast unique identifier for the video.","title":"Bitcast Video Id"},"description":"The Bitcast unique identifier for the video."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SingleVideoResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/{portal_user_id}/videos":{"get":{"tags":["v1-videos (LEGACY - migrate to /api/v2/youtube/videos)"],"summary":"Get User Videos","description":"Retrieve videos for a specific user with calculated metrics.\n\nThis endpoint returns videos belonging to a specific portal user, filtered by publication date,\nwith complex calculated metrics including brief matching data, earnings, and performance metrics.\n\n**Caching:**\n- Response is cached for 5 minutes (300 seconds)\n- Cache key includes portal_user_id and date_lookback parameters\n\n**Path Parameters:**\n- **portal_user_id**: The portal user ID to retrieve videos for\n\n**Query Parameters:**\n- **date_lookback**: Days to look back for video selection (default: 30, min: 1, max: 365)\n\n**Returns:**\n- List of user videos with calculated metrics\n- Only includes videos with privacy_status = 'public'\n- Date filter applies only to video selection, not metric calculations\n\n**Calculated Fields:**\n- **brief_id**: Brief ID if video matched to brief (null otherwise)\n- **daily_earnings**: Average usd_target from validator runs in past 24 hours from request time (with weight corrections applied)\n- **usd_total**: Sum of daily averages for usd_target across entire video history (with weight corrections applied)\n- **alpha_total**: Sum of daily averages for alpha_target across entire video history (with weight corrections applied)\n- **cpm**: Cost per mille (CPM) calculated as (usd_total / latest_views) * 1000\n- **status**: Video monetization status:\n    - \"Earning\": Video matched to brief and published ≤ 3 weeks ago\n    - \"Complete\": Video matched to brief and published > 3 weeks ago  \n    - \"Non-monotized\": Video not matched to any brief\n\n**Response Format:**\n```json\n{\n    \"videos\": [\n        {\n            \"id_video\": 123,\n            \"bitcast_video_id\": \"vid_abc123\",\n            \"title\": \"Sample Video\",\n            \"published_at\": \"2025-01-15T10:30:00Z\",\n            \"brief_id\": \"brief_001\",\n            \"daily_earnings\": 25.50,\n            \"usd_total\": 150.75,\n            \"alpha_total\": 1250.25,\n            \"cpm\": 150.75,\n            \"status\": \"Earning\"\n        }\n    ],\n    \"total_count\": 1,\n    \"date_lookback_applied\": 30\n}\n```\n\n**Errors:**\n- **404**: Portal user not found\n- **500**: Database error occurred\n- **503**: Service temporarily unavailable","operationId":"get_user_videos_api_v1_users__portal_user_id__videos_get","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","description":"Portal user ID to retrieve videos for","title":"Portal User Id"},"description":"Portal user ID to retrieve videos for"},{"name":"date_lookback","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"Number of days to look back for video selection (based on published_at date)","default":30,"title":"Date Lookback"},"description":"Number of days to look back for video selection (based on published_at date)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/{portal_user_id}/account/stats":{"get":{"tags":["v1-videos (LEGACY - migrate to /api/v2/youtube/videos)"],"summary":"Get Account Stats","description":"Retrieve account-level aggregated statistics across all user videos with brief matches.\n\nThis endpoint returns aggregated metrics for an entire account, summing data across\nall videos that exist in the video_matched_to_brief table (i.e., videos with brief matches).\n\n**Caching:**\n- Response is cached for 5 minutes (300 seconds)\n- Cache key includes portal_user_id and lookback_days parameters\n\n**Path Parameters:**\n- **portal_user_id**: Portal user ID for the account\n\n**Query Parameters:**\n- **lookback_days**: Days to look back for totals calculation (default: unlimited, min: 1)\n\n**Returns:**\nAccount-level aggregated statistics with calculated metrics\n\n**Calculated Fields:**\n- **daily_earnings**: Sum of per-video averages from past 24 hours (with weight corrections)\n- **usd_total**: Sum of daily averages across all account videos within lookback period (with weight corrections)  \n- **alpha_total**: Sum of daily averages across all account videos within lookback period (with weight corrections)\n- **cpm**: Cost per mille calculated as (total_usd_targets / total_views * 1000) over lookback period\n- **outstanding_balance_alpha**: Outstanding balance in Alpha tokens from payments with status 'staged' or 'failed'\n- **account_title**: Account title from yt_account.title field\n\n**Scope:**\n- Only includes videos that exist in video_matched_to_brief table\n- Applies same weight corrections as individual video APIs\n- Returns zero values for accounts with no videos or missing data (200 OK)\n\n**Performance:**\n- Response cached for 5 minutes\n- Target response time: < 500ms for typical account sizes","operationId":"get_account_stats_api_v1_users__portal_user_id__account_stats_get","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","description":"Portal user ID for account stats retrieval","title":"Portal User Id"},"description":"Portal user ID for account stats retrieval"},{"name":"lookback_days","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Number of days to look back for totals calculation (default: unlimited)","title":"Lookback Days"},"description":"Number of days to look back for totals calculation (default: unlimited)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountStatsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/videos/health":{"get":{"tags":["v1-videos (LEGACY - migrate to /api/v2/youtube/videos)","health"],"summary":"Videos Service Health","description":"Health check endpoint for the videos service.\n\nPerforms a basic database connectivity test to ensure the service is operational.\n\n**Returns:**\n- Service status and database connectivity information\n\n**Response Format:**\n```json\n{\n    \"service\": \"videos\",\n    \"status\": \"healthy\",\n    \"timestamp\": \"2025-01-29T12:00:00Z\",\n    \"database\": {\n        \"connected\": true,\n        \"response_time_ms\": 45\n    }\n}\n```\n\n**Status Codes:**\n- **200**: Service is healthy\n- **503**: Service is unhealthy (database issues)\n- **500**: Internal server error","operationId":"videos_service_health_api_v1_videos_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoHealthResponse"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/script-checker":{"post":{"tags":["v1-script-checker (LEGACY - migrate to /api/v2/youtube/validation/script)"],"summary":"Check Script","description":"Check script content against a brief using 3 concurrent calls to bitcast internal API.\n\nThis endpoint makes 3 concurrent evaluations and returns the most pessimistic result\nto account for LLM non-determinism. If any evaluation returns false, the overall \nresult is false (fail-safe approach).\n\n**Parameters:**\n- **brief_id**: Brief identifier to lookup in database\n- **transcript**: Video transcript content (max 70,000 characters)  \n- **description**: YouTube video description (max 10,000 characters)\n- **duration**: Video duration (format: \"00:08:27\")\n\n**Returns:**\n- Script check result with match status, reasoning, and metadata\n\n**Response Format:**\n```json\n{\n    \"brief_id\": \"test-brief-123\",\n    \"match\": true,\n    \"reasoning\": \"Detailed checking reasoning...\"\n}\n```\n\n**Errors:**\n- **400**: Validation error (input too long, missing fields, invalid duration format)\n- **500**: Internal server error or bitcast API unavailable","operationId":"check_script_api_v1_script_checker_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScriptCheckerRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScriptCheckerResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/script-checker/health":{"get":{"tags":["v1-script-checker (LEGACY - migrate to /api/v2/youtube/validation/script)","health"],"summary":"Script Checker Health","description":"Health check for the script checker service.\n\nTests connectivity to the bitcast internal API and returns status information.\n\n**Returns:**\n- Service health status and bitcast API availability\n- Response time metrics\n- Error details if unhealthy\n\n**Response Format:**\n```json\n{\n    \"status\": \"healthy\",\n    \"bitcast_api_available\": true,\n    \"response_time_ms\": 45.2,\n    \"error\": null\n}\n```","operationId":"script_checker_health_api_v1_script_checker_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScriptCheckerHealthResponse"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/twitter":{"get":{"tags":["twitter"],"summary":"Get Twitter account scores by pool and date","description":"Get Twitter account scores for a specific pool as of a given date.\n\nThis endpoint finds the most recent validator run on or before the specified date,\nthen returns all Twitter account scores for that pool from that validator run.\n\n**Important:** Not all validator runs have x_account_scores associated. This endpoint\nautomatically finds the most recent validator run that has scores available.\n\n**Parameters:**\n- **pool**: Pool name (e.g., \"elite\", \"challenger\", \"rising\")\n- **date**: (Optional) Query date in YYYY-MM-DD format (finds most recent validator run <= this date). If not provided, returns most recent scores available.\n- **limit**: (Optional) Limit results to top N accounts, sorted by score descending (minimum: 1)\n\n**Example Request:**\n```\nGET /api/v1/twitter?pool=elite&date=2025-10-10&limit=50\n```\n\n**Example Response (scores found):**\n```json\n{\n  \"pool\": \"elite\",\n  \"query_date\": \"2025-10-10\",\n  \"validator_run_date\": \"2025-10-09T14:30:00\",\n  \"scores\": [\n    {\n      \"username\": \"user1\",\n      \"bitcast_account_id\": \"bc_account_1\",\n      \"connected\": true,\n      \"followers\": 1250,\n      \"score\": 0.95432,\n      \"validator_run_date\": \"2025-10-09T14:30:00\"\n    }\n  ],\n  \"total_count\": 1,\n  \"total_before_limit\": 150,\n  \"adjacency_matrix\": {\n    \"usernames\": [\"user1\", \"user2\"],\n    \"dimensions\": [2, 2],\n    \"adjacency_matrix\": [[0, 1], [1, 0]]\n  }\n}\n```\n\n**Response Fields:**\n- **total_count**: Number of accounts returned (after applying limit if specified)\n- **total_before_limit**: Total accounts available before limit (null if no limit applied)\n\n**Status Codes:**\n- **200**: Request processed successfully (may return empty scores with message)\n- **400**: Invalid date format, missing parameters, or limit < 1\n- **500**: Database error occurred\n\n**Caching:**\n- Response is cached for 10 minutes (600 seconds)\n- Cache key includes pool, date, include_matrix, and limit parameters\n\n**Response Size:**\n- Default (include_matrix=False): ~12 KB\n- With matrix (include_matrix=True): ~35 KB (matrix can be 20-500KB for large pools)","operationId":"get_twitter_scores_by_pool_and_date_api_v1_twitter_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"pool","in":"query","required":true,"schema":{"type":"string","description":"Pool name to query scores for (e.g., 'elite', 'challenger')","title":"Pool"},"description":"Pool name to query scores for (e.g., 'elite', 'challenger')"},{"name":"date","in":"query","required":false,"schema":{"type":"string","description":"Optional date in YYYY-MM-DD format (finds most recent run <= this date). If not provided, returns most recent scores available.","title":"Date"},"description":"Optional date in YYYY-MM-DD format (finds most recent run <= this date). If not provided, returns most recent scores available."},{"name":"include_matrix","in":"query","required":false,"schema":{"type":"boolean","description":"Include adjacency matrix in response (default: False). Set to True to include social network graph data.","default":false,"title":"Include Matrix"},"description":"Include adjacency matrix in response (default: False). Set to True to include social network graph data."},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Limit results to top N accounts (sorted by score descending)","title":"Limit"},"description":"Limit results to top N accounts (sorted by score descending)"}],"responses":{"200":{"description":"Successfully retrieved Twitter account scores (may be empty)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterAccountScoresResponse"}}}},"400":{"description":"Invalid request parameters"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/twitter/pools":{"get":{"tags":["twitter"],"summary":"[LEGACY] Get list of available Twitter pools","description":"**[LEGACY - Use /twitter/pool-configs instead]**\n\nGet list of available Twitter pool names from the x_pools table.\n\n**This endpoint is deprecated.** Please use `/twitter/pool-configs` for complete pool information \nincluding keywords, initial_accounts, constraints, and all other configuration details.\n\nBy default, returns only active pool names. Set `include_inactive=true` to include inactive pools.\n\n**Parameters:**\n- **include_inactive** (optional): If `true`, return all pools including inactive ones. Default is `false` (active only).\n\n**Example Request (active only):**\n```\nGET /api/v1/twitter/pools\n```\n\n**Example Request (all pools):**\n```\nGET /api/v1/twitter/pools?include_inactive=true\n```\n\n**Example Response:**\n```json\n{\n  \"pools\": [\"elite\", \"challenger\", \"rising\"],\n  \"total_count\": 3\n}\n```\n\n**Status Codes:**\n- **200**: Request processed successfully (returns empty list if no pools exist)\n- **500**: Database error occurred\n\n**Caching:**\n- Response is cached for 10 minutes (600 seconds)\n- Separate cache keys for active-only vs all pools\n\n**Migration Guide:**\n- Replace: `GET /api/v1/twitter/pools`\n- With: `GET /api/v1/twitter/pool-configs`\n- The new endpoint returns full pool configuration objects instead of just names","operationId":"get_available_pools_api_v1_twitter_pools_get","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"include_inactive","in":"query","required":false,"schema":{"type":"boolean","description":"Include inactive pools (default: False, returns only active pools)","default":false,"title":"Include Inactive"},"description":"Include inactive pools (default: False, returns only active pools)"}],"responses":{"200":{"description":"Successfully retrieved pool list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterPoolsResponse"}}}},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/twitter/pool-configs":{"get":{"tags":["twitter"],"summary":"Get Twitter pool configurations (all x_pools fields)","description":"Get detailed configurations for Twitter pools from the x_pools table.\n\n**This is the recommended endpoint for pool information.** Returns all x_pools table fields including:\n- Pool ID and name\n- Keywords to track\n- Initial seed accounts\n- Core tier constraints (core_max_seed_accounts, core_min_interaction_weight, core_min_tweets)\n- Extended tier constraints (extended_max_seed_accounts, extended_min_interaction_weight, extended_min_tweets)\n- Discovery settings (max_discovery_iterations, convergence_threshold)\n- Language filter\n- Active status\n- Timestamps\n\nBy default, returns only active pools. Set `include_inactive=true` to include inactive pools.\n\n**Parameters:**\n- **include_inactive** (optional): If `true`, return all pools including inactive ones. Default is `false` (active only).\n\n**Example Request (active only):**\n```\nGET /api/v1/twitter/pool-configs\n```\n\n**Example Request (all pools):**\n```\nGET /api/v1/twitter/pool-configs?include_inactive=true\n```\n\n**Example Response:**\n```json\n{\n  \"pools\": [\n    {\n      \"id_x_pools\": 1,\n      \"name\": \"elite\",\n      \"keywords\": [\"crypto\", \"blockchain\"],\n      \"initial_accounts\": [\"user1\", \"user2\"],\n      \"lang\": \"en\",\n      \"active\": true,\n      \"date_offset\": 0,\n      \"core_min_interaction_weight\": 5,\n      \"core_min_tweets\": 10,\n      \"core_max_seed_accounts\": 100,\n      \"extended_min_interaction_weight\": 1,\n      \"extended_min_tweets\": 1,\n      \"extended_max_seed_accounts\": 300,\n      \"max_discovery_iterations\": 3,\n      \"convergence_threshold\": 0.95,\n      \"created_at\": \"2025-01-01T00:00:00\",\n      \"updated_at\": \"2025-01-01T00:00:00\"\n    }\n  ],\n  \"total_count\": 1\n}\n```\n\n**Status Codes:**\n- **200**: Request processed successfully (returns empty list if no pools exist)\n- **500**: Database error occurred\n\n**Caching:**\n- Response is cached for 10 minutes (600 seconds)\n- Separate cache keys for active-only vs all pools","operationId":"get_pool_configurations_api_v1_twitter_pool_configs_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"include_inactive","in":"query","required":false,"schema":{"type":"boolean","description":"Include inactive pools (default: False, returns only active pools)","default":false,"title":"Include Inactive"},"description":"Include inactive pools (default: False, returns only active pools)"}],"responses":{"200":{"description":"Successfully retrieved pool configurations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoolConfigsResponse"}}}},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/twitter/accounts":{"get":{"tags":["twitter"],"summary":"Get all Twitter accounts across all pools","description":"Get all Twitter accounts across all pools with no duplicates.\n\nReturns accounts that are current members of at least one pool from the most recent validator run.\nEach account includes:\n- Username (Twitter handle)\n- Bitcast account ID\n- List of pools the account is a member of\n- Highest score across all pools\n- Follower count\n- Connected status (whether account has records in x_connections)\n\n**Example Request:**\n```\nGET /api/v1/twitter/accounts\n```\n\n**Example Response:**\n```json\n{\n  \"accounts\": [\n    {\n      \"username\": \"cryptouser1\",\n      \"bitcast_account_id\": \"bc_account_123\",\n      \"pools\": [\"elite\", \"challenger\"],\n      \"score\": 0.95432,\n      \"followers\": 1250,\n      \"connected\": true\n    },\n    {\n      \"username\": \"cryptouser2\",\n      \"bitcast_account_id\": \"bc_account_456\",\n      \"pools\": [\"rising\"],\n      \"score\": 0.82145,\n      \"followers\": 890,\n      \"connected\": false\n    }\n  ],\n  \"total_count\": 2,\n  \"validator_run_date\": \"2025-12-08T10:30:00\"\n}\n```\n\n**Status Codes:**\n- **200**: Request processed successfully (returns empty list if no accounts found)\n- **500**: Database error occurred\n\n**Caching:**\n- Response is cached for 10 minutes (600 seconds)\n\n**Notes:**\n- Accounts are sorted by highest score (descending)\n- Each account appears only once (deduplicated by account ID)\n- Only includes accounts from the most recent validator run\n- Does not include adjacency matrix data","operationId":"get_all_accounts_api_v1_twitter_accounts_get","responses":{"200":{"description":"Successfully retrieved all accounts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterAllAccountsResponse"}}}},"500":{"description":"Internal server error"}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/twitter/register-coldkey/{coldkey}":{"post":{"tags":["twitter"],"summary":"Register a coldkey and get registration tag (legacy)","description":"**[DEPRECATED]** Register a coldkey and receive a unique registration tag.\n\n**This endpoint is deprecated. Please use `POST /twitter/register-coldkey` (without path parameter) instead,\nwhich supports additional fields like token and referred_by.**\n\nThis endpoint is **idempotent** - if the coldkey is already registered,\nit will return the existing tag. If not, it creates a new entry and returns\na new tag.\n\nThe registration tag is generated deterministically using SHA-256 hash:\n`tag = f\"Stitch3-{sha256(coldkey)[:8]}\"`\n(Legacy endpoint does not support referral suffix)\n\n**Coldkey Format:**\n- Bittensor substrate wallet address\n- 40-50 characters in length\n- Base58 encoded (alphanumeric, excluding 0, O, I, l)\n\n**Example Request:**\n```\nPOST /api/v1/twitter/register-coldkey/5GHqJxPX5aYqKMT7JNaZJcKmMbNRpP8JCYHqJxPX5aYqKMT7\n```\n\n**Example Response:**\n```json\n{\n  \"tag\": \"Stitch3-a3c5e8f1\"\n}\n```\n\n**Idempotency:**\nCalling this endpoint multiple times with the same coldkey will always\nreturn the same tag, ensuring safe retry behavior.\n\n**Args:**\n- **coldkey**: The substrate wallet address to register (in URL path)\n\n**Returns:**\n- **tag**: The registration tag for the coldkey\n\n**Raises:**\n- **422**: Invalid coldkey format (validation error)\n- **500**: Database error or unexpected server error","operationId":"register_coldkey_legacy_api_v1_twitter_register_coldkey__coldkey__post","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"coldkey","in":"path","required":true,"schema":{"type":"string","minLength":40,"maxLength":50,"pattern":"^[1-9A-HJ-NP-Za-km-z]{40,50}$","description":"Bittensor substrate wallet address (coldkey)","title":"Coldkey"},"description":"Bittensor substrate wallet address (coldkey)"}],"responses":{"200":{"description":"Successfully registered coldkey (or returned existing tag)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ColdkeyRegistrationResponse"},"example":{"tag":"Stitch3-a3c5e8f1"}}}},"422":{"description":"Invalid coldkey format","content":{"application/json":{"example":{"detail":[{"loc":["path","coldkey"],"msg":"Coldkey must contain only valid base58 characters","type":"value_error"}]}}}},"500":{"description":"Internal server error"}}}},"/api/v1/twitter/register-coldkey":{"post":{"tags":["twitter"],"summary":"Register a coldkey with additional metadata and get registration tag","description":"Register a coldkey and receive a unique registration tag.\n\nThis endpoint is **idempotent** on the coldkey + referral combination.\nThe same coldkey can generate multiple tags with different referrals.\nIf the exact coldkey + referral combination already exists, returns the existing tag.\n\nThe registration tag is generated deterministically using SHA-256 hash:\n`tag = f\"Stitch3-{sha256(coldkey)[:8]}\"`\nIf referred_by is provided: `tag = f\"Stitch3-{sha256(coldkey)[:8]}-{referred_by}\"`\n\n**Coldkey Format:**\n- Bittensor substrate wallet address\n- 40-50 characters in length\n- Base58 encoded (alphanumeric, excluding 0, O, I, l)\n\n**Example Request:**\n```json\n{\n  \"coldkey\": \"5GHqJxPX5aYqKMT7JNaZJcKmMbNRpP8JCYHqJxPX5aYqKMT7\",\n  \"token\": \"bitcast\",\n  \"referred_by\": \"user123\"\n}\n```\n\n**Example Response (with referral):**\n```json\n{\n  \"tag\": \"Stitch3-a3c5e8f1-user123\"\n}\n```\n\n**Example Response (without referral):**\n```json\n{\n  \"tag\": \"Stitch3-a3c5e8f1\"\n}\n```\n\n**Idempotency:**\nCalling this endpoint multiple times with the same coldkey will always\nreturn the same tag, ensuring safe retry behavior.\n\n**Args:**\n- **coldkey**: The substrate wallet address to register (required)\n- **token**: Token for no-code coldkey (required)\n- **referred_by**: Referral information (optional)\n\n**Returns:**\n- **tag**: The registration tag for the coldkey\n\n**Raises:**\n- **422**: Invalid coldkey format (validation error)\n- **500**: Database error or unexpected server error","operationId":"register_coldkey_api_v1_twitter_register_coldkey_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ColdkeyRegistrationRequest"}}},"required":true},"responses":{"200":{"description":"Successfully registered coldkey (or returned existing tag)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ColdkeyRegistrationResponse"},"example":{"tag":"Stitch3-a3c5e8f1"}}}},"422":{"description":"Invalid coldkey format","content":{"application/json":{"example":{"detail":[{"loc":["body","coldkey"],"msg":"Coldkey must contain only valid base58 characters","type":"value_error"}]}}}},"500":{"description":"Internal server error"}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/twitter/briefs":{"get":{"tags":["twitter"],"summary":"Get all Twitter briefs with key details","description":"Get list of all Twitter briefs with key metrics, optionally filtered by pool.\n\nReturns brief details including:\n- Brief ID and description\n- Post count (number of tweets matched)\n- Total views across all tweets\n- Budget information (from active version)\n- Maximum tweets, members, and considered limits\n- Start and end dates (from active version)\n- Status (Upcoming/Live/Complete)\n- Pools (array), tag, and QRT information\n\n**Parameters:**\n- **pool** (optional): Pool name to filter briefs by (e.g., \"elite\", \"challenger\")\n  - Filters briefs that include this pool in their pools array\n\n**Example Request:**\n```\nGET /api/v1/twitter/briefs?pool=elite\n```\n\n**Example Response:**\n```json\n{\n  \"briefs\": [\n    {\n      \"brief_id\": \"crypto_12345\",\n      \"brief\": \"Promote Product X\",\n      \"Posts\": 45,\n      \"views\": 125000,\n      \"budget\": 5000,\n      \"max_tweets\": 1,\n      \"max_members\": 150,\n      \"max_considered\": 300,\n      \"start_date\": \"2025-12-15\",\n      \"end_date\": \"2025-12-20\",\n      \"status\": \"Live\",\n      \"pools\": [\"elite\", \"challenger\"],\n      \"tag\": \"crypto\",\n      \"qrt\": \"https://twitter.com/example/status/123456789\"\n    }\n  ],\n  \"total_count\": 1\n}\n```\n\n**Note:** Budget, start_date, end_date, and max_tweets come from the active version of the brief.","operationId":"get_twitter_briefs_api_v1_twitter_briefs_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"pool","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional pool name to filter briefs (e.g., 'elite', 'challenger')","title":"Pool"},"description":"Optional pool name to filter briefs (e.g., 'elite', 'challenger')"}],"responses":{"200":{"description":"Successfully retrieved Twitter briefs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterBriefsListResponse"}}}},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["twitter"],"summary":"Create a user-generated brief with funding","description":"Create a new user-generated Twitter brief with funding calculation.\n\nThis endpoint creates a brief and returns payment details including:\n- Destination address for payment\n- Crypto amount to send\n- Exchange rate used\n- Payment expiration time (15 minutes)\n\n**Returns:**\n- Brief details with funding_id (needed to submit payment)","operationId":"create_brief_with_funding_api_v1_twitter_briefs_post","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBriefFundingRequest"}}}},"responses":{"201":{"description":"Brief created successfully with funding record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefFundingResponse"}}}},"400":{"description":"Invalid request parameters or validation failed"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/twitter/briefs/{brief_id}":{"get":{"tags":["twitter"],"summary":"Get detailed information for a specific Twitter brief","description":"Get detailed information for a specific Twitter brief.\n\nIncludes:\n- Brief description and display name\n- Budget details (from active version)\n- Start and end dates (from active version)\n- Status (Upcoming/Live/Complete)\n- Pools (array), tag, and QRT information\n- Performance metrics (posts, views, favorites, retweets, replies, quotes, bookmarks)\n- List of tweets matched to this brief from most recent validator run (ordered by weight DESC)\n  Each tweet includes:\n  - id: Tweet ID (as string to preserve precision)\n  - username: X/Twitter username/handle of the tweet author\n  - reward: USD reward for this tweet\n  - score: Weight score\n  - views, favourites, retweets, replies, quotes, bookmarks: Engagement metrics\n  - retweeted_by: List of users who retweeted (JSON array)\n  - quoted_by: List of users who quoted (JSON array)\n  - content: Tweet text content\n\n**Note:** \n- Only tweets from the most recent validator run are included.\n- Budget, dates, and max_tweets come from the active version of the brief.","operationId":"get_twitter_brief_details_api_v1_twitter_briefs__brief_id__get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","description":"Brief ID to get details for","title":"Brief Id"},"description":"Brief ID to get details for"}],"responses":{"200":{"description":"Successfully retrieved brief details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterBriefDetailResponse"}}}},"404":{"description":"Brief not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/twitter/briefs/{brief_id}/analytics":{"get":{"tags":["twitter"],"summary":"Get engagement analytics for a specific Twitter brief","description":"Get engagement analytics data for a specific Twitter brief.\n\nIncludes:\n- Cumulative time-series metrics (posts, favorites, retweets, quotes, replies, bookmarks)\n- Data timestamp\n\n**Note:** All timeseries metrics are cumulative (each date shows running total, not daily values)","operationId":"get_twitter_brief_analytics_api_v1_twitter_briefs__brief_id__analytics_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","description":"Brief ID to get analytics for","title":"Brief Id"},"description":"Brief ID to get analytics for"}],"responses":{"200":{"description":"Successfully retrieved analytics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterBriefAnalyticsResponse"}}}},"404":{"description":"Brief not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/twitter/briefs/{brief_id}/topup":{"post":{"tags":["twitter"],"summary":"Top up an existing brief with additional funding","description":"Top up an existing brief with additional funding.\n\nThis endpoint creates a new version of the brief with increased budget and returns\npayment details including:\n- Destination address for payment\n- Crypto amount to send\n- Exchange rate used\n- Payment expiration time (15 minutes)\n\n**Returns:**\n- Brief details with funding_id (needed to submit payment)","operationId":"topup_brief_funding_api_v1_twitter_briefs__brief_id__topup_post","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","title":"Brief Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TopUpBriefFundingRequest"}}}},"responses":{"200":{"description":"Top-up created successfully with funding record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefFundingResponse"}}}},"400":{"description":"Invalid request parameters or validation failed"},"404":{"description":"Brief not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/twitter/briefs/funding/{funding_id}/payment":{"post":{"tags":["twitter"],"summary":"Submit payment transaction hash and block number","description":"Submit payment transaction hash and block number to complete brief funding.\n\n**Validation checks (for TAO transactions):**\n- Transaction exists at the specified block number\n- Transaction was successful on-chain\n- Destination address matches expected address\n- Amount matches expected amount (within 0.1% tolerance)\n- Transaction is recent (< 1 hour old)\n- Transaction hash hasn't been used before\n\n**On success:**\n- Marks funding as 'complete'\n- Activates the brief\n- Returns confirmation message\n\n**On failure:**\n- Returns detailed error message explaining what went wrong\n- Funding remains in 'pending' state (can retry with valid transaction)","operationId":"submit_brief_payment_api_v1_twitter_briefs_funding__funding_id__payment_post","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"funding_id","in":"path","required":true,"schema":{"type":"integer","title":"Funding Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitPaymentRequest"}}}},"responses":{"200":{"description":"Payment validated and brief activated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitPaymentResponse"}}}},"400":{"description":"Payment validation failed (invalid transaction, wrong amount, expired, etc.)"},"404":{"description":"Funding record not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/twitter/briefs/funding/{funding_id}/cancel":{"post":{"tags":["twitter"],"summary":"Cancel a pending funding record","description":"Cancel a pending funding record.\n\nThis endpoint allows users to cancel a funding request when they navigate away\nfrom the payment page or decide not to proceed. Only funding records with \n'pending' status can be cancelled.\n\n**Actions:**\n- Marks funding status as 'cancelled'\n- Marks associated brief version as 'cancelled'\n- Allows user to create a new funding request without hitting the \"multiple pending\" validation\n\n**Note:** Cannot cancel funding that is already 'complete', 'expired', or 'failed'.","operationId":"cancel_brief_funding_api_v1_twitter_briefs_funding__funding_id__cancel_post","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"funding_id","in":"path","required":true,"schema":{"type":"integer","title":"Funding Id"}}],"responses":{"200":{"description":"Funding cancelled successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelFundingResponse"}}}},"400":{"description":"Cannot cancel funding (not in pending status or validation failed)"},"404":{"description":"Funding record not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notifications/health":{"get":{"tags":["v1-notifications (LEGACY - migrate to /api/v2/youtube/notifications)","health"],"summary":"Notifications Health","description":"Health check endpoint for the notifications service.\n\nTests database connectivity specifically for notification operations.\n\n**Returns:**\n```json\n{\n    \"status\": \"healthy\",\n    \"service\": \"notifications\",\n    \"database_accessible\": true,\n    \"timestamp\": 1642682400.0\n}\n```\n\n**Status Codes:**\n- **200**: Service is healthy\n- **503**: Service is unhealthy (database issues)","operationId":"notifications_health_api_v1_notifications_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceHealthResponse"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/notifications/queue/unprocessed":{"get":{"tags":["v1-notifications (LEGACY - migrate to /api/v2/youtube/notifications)"],"summary":"Get Unprocessed Notifications","description":"Retrieve unprocessed notifications from the queue.\n\nReturns notifications where processed=0, ordered by created_at DESC (newest first).\n\n**Query Parameters:**\n- **platform**: Optional filter by platform (portal, telegram, or email)\n\n**Example Response:**\n```json\n{\n    \"notifications\": [\n        {\n            \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n            \"platform\": \"email\",\n            \"portal_user_ids\": [1, 2, 3],\n            \"message\": \"Your payment has been processed\",\n            \"redirect_link\": \"https://portal.example.com/payments\",\n            \"processed\": false,\n            \"created_at\": \"2025-11-07T10:00:00Z\"\n        }\n    ],\n    \"total_count\": 1\n}\n```\n\n**Use Case:**\nClient fetches unprocessed notifications, processes them (sends emails/telegrams/etc),\nthen logs results using POST /notifications/log/bulk endpoint.\n\n**Status Codes:**\n- **200**: Successfully retrieved notifications (may be empty array)\n- **500**: Database error occurred","operationId":"get_unprocessed_notifications_api_v1_notifications_queue_unprocessed_get","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"platform","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/NotificationPlatform"},{"type":"null"}],"description":"Filter by platform (portal/telegram/email)","title":"Platform"},"description":"Filter by platform (portal/telegram/email)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnprocessedQueueResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notifications/queue":{"post":{"tags":["v1-notifications (LEGACY - migrate to /api/v2/youtube/notifications)"],"summary":"Create Notification Queue Item","description":"**⚠️ TEMPORARY DEV/TESTING ENDPOINT ⚠️**\n\nCreate a real notification in the queue for development/testing purposes.\n\nThis endpoint creates actual notifications without requiring business events\n(like payment processing or brief creation). This enables independent development\nand testing of notification delivery systems (email/telegram/portal senders).\n\n**Example Request:**\n```json\n{\n    \"platform\": \"email\",\n    \"portal_user_ids\": [1, 2, 3],\n    \"message\": \"Test notification - payment processed\",\n    \"redirect_link\": \"https://portal.example.com/payments\"\n}\n```\n\n**Example Response:**\n```json\n{\n    \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"platform\": \"email\",\n    \"portal_user_ids\": [1, 2, 3],\n    \"message\": \"Test notification - payment processed\",\n    \"redirect_link\": \"https://portal.example.com/payments\",\n    \"processed\": false,\n    \"created_at\": \"2025-11-25T10:00:00Z\"\n}\n```\n\n**Status Codes:**\n- **201**: Notification created successfully\n- **400**: Invalid request data (e.g., invalid user IDs)\n- **500**: Database error occurred\n\n**Workflow:**\n1. Create test notification using this endpoint\n2. Fetch it using GET /notifications/queue/unprocessed\n3. Process it (send emails/telegrams/etc)\n4. Log results using POST /notifications/log/bulk","operationId":"create_notification_queue_item_api_v1_notifications_queue_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNotificationQueueRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNotificationQueueResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/notifications/log/bulk":{"post":{"tags":["v1-notifications (LEGACY - migrate to /api/v2/youtube/notifications)"],"summary":"Create Notification Logs Bulk","description":"Create multiple notification delivery log entries in one call.\n\nRecords delivery status for multiple users for the same notification.\nAutomatically marks queue as processed when ALL users from portal_user_ids have log entries.\n\n**Use Case:** Process a notification batch for multiple users in a single API call\ninstead of making individual calls per user (much more efficient).\n\n**Example Request:**\n```json\n{\n    \"logs\": [\n        {\n            \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n            \"user_id\": 1,\n            \"success\": true,\n            \"error_msg\": null\n        },\n        {\n            \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n            \"user_id\": 2,\n            \"success\": false,\n            \"error_msg\": \"Email bounced\"\n        },\n        {\n            \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n            \"user_id\": 3,\n            \"success\": true,\n            \"error_msg\": null\n        }\n    ]\n}\n```\n\n**Example Response:**\n```json\n{\n    \"logs\": [\n        {\n            \"id_notification_log\": 123,\n            \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n            \"user_id\": 1,\n            \"success\": true,\n            \"error_msg\": null,\n            \"timestamp\": \"2025-11-07T10:05:00Z\"\n        },\n        {\n            \"id_notification_log\": 124,\n            \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n            \"user_id\": 2,\n            \"success\": false,\n            \"error_msg\": \"Email bounced\",\n            \"timestamp\": \"2025-11-07T10:05:00Z\"\n        },\n        {\n            \"id_notification_log\": 125,\n            \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n            \"user_id\": 3,\n            \"success\": true,\n            \"error_msg\": null,\n            \"timestamp\": \"2025-11-07T10:05:00Z\"\n        }\n    ],\n    \"total_created\": 3,\n    \"queue_processed\": true,\n    \"queue_fully_logged\": true\n}\n```\n\n**Status Codes:**\n- **201**: Log entries created successfully\n- **404**: Notification ID or user ID not found\n- **400**: Invalid request data or empty logs array\n- **500**: Database error occurred (all or nothing - transaction rolled back)\n\n**Processing Logic:**\n1. Validates all log entries\n2. Inserts all logs in a single transaction\n3. Counts total users in queue's portal_user_ids JSON array\n4. Counts total log entries for this notification_id (including newly inserted)\n5. If counts match, automatically marks queue.processed = 1\n6. Returns all created logs with queue status\n\n**Performance:** One API call can log results for 100+ users efficiently.","operationId":"create_notification_logs_bulk_api_v1_notifications_log_bulk_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkCreateNotificationLogRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkNotificationLogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/performance/chutes-test":{"post":{"tags":["v1-performance-monitoring (DEPRECATED - use /api/v2/monitoring)"],"summary":"Test Chutes Performance","description":"Test Chutes AI model performance metrics.\n\nMeasures performance of the MiniMaxAI/MiniMax-M2.1-TEE model:\n- **Time to First Token (TTFT)**: Latency from request to first token\n- **Tokens Per Second**: Average token generation speed\n\n**Parameters:**\n- **prompt**: The prompt to send to the model (default: \"Tell me a 250 word story.\")\n- **max_tokens**: Maximum tokens to generate (1-4096, default: 1024)\n- **temperature**: Temperature for generation (0.0-2.0, default: 0.7)\n\n**Returns:**\n```json\n{\n    \"model\": \"Qwen/Qwen3-32B\",\n    \"time_to_first_token_ms\": 234.56,\n    \"tokens_per_second\": 45.23,\n    \"total_tokens\": 256,\n    \"total_time_ms\": 5678.90,\n    \"prompt\": \"Tell me a 250 word story.\"\n}\n```\n\n**Errors:**\n- **400**: Invalid request parameters or API key not configured\n- **500**: API request failed or unexpected error\n- **504**: Request timed out (60 second timeout)","operationId":"test_chutes_performance_api_v1_performance_chutes_test_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChutesPerformanceRequest","default":{"prompt":"Tell me a 250 word story.","max_tokens":1024,"temperature":0.7}}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChutesPerformanceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/performance/twitter-latency":{"post":{"tags":["v1-performance-monitoring (DEPRECATED - use /api/v2/monitoring)"],"summary":"Test Twitter Latency","description":"Test Twitter API latency via RapidAPI.\n\nMeasures the response time for fetching user tweets from the Twitter API.\n\n**Parameters:**\n- **username**: Twitter username to fetch tweets for (default: \"elonmusk\")\n- **limit**: Number of tweets to fetch (1-100, default: 40)\n\n**Returns:**\n```json\n{\n    \"endpoint\": \"twitter-v24.p.rapidapi.com/user/tweets?username=elonmusk&limit=40\",\n    \"latency_ms\": 1234.56,\n    \"status_code\": 200,\n    \"username\": \"elonmusk\",\n    \"limit\": 40,\n    \"success\": true\n}\n```\n\n**Errors:**\n- **400**: Invalid request parameters or API key not configured\n- **500**: API request failed or unexpected error","operationId":"test_twitter_latency_api_v1_performance_twitter_latency_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterLatencyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterLatencyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/performance/youtube-latency":{"post":{"tags":["v1-performance-monitoring (DEPRECATED - use /api/v2/monitoring)"],"summary":"Test Youtube Latency","description":"Test YouTube Transcriptor API latency via RapidAPI.\n\nMeasures the response time for fetching video transcripts from the YouTube API.\n\n**Parameters:**\n- **video_id**: YouTube video ID to fetch transcript for (default: \"8aGhZQkoFbQ\")\n- **lang**: Language code for transcript (default: \"en\")\n\n**Returns:**\n```json\n{\n    \"endpoint\": \"youtube-transcriptor.p.rapidapi.com/transcript?video_id=8aGhZQkoFbQ&lang=en\",\n    \"latency_ms\": 987.65,\n    \"status_code\": 200,\n    \"video_id\": \"8aGhZQkoFbQ\",\n    \"lang\": \"en\",\n    \"success\": true\n}\n```\n\n**Errors:**\n- **400**: Invalid request parameters or API key not configured\n- **500**: API request failed or unexpected error","operationId":"test_youtube_latency_api_v1_performance_youtube_latency_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/YouTubeLatencyRequest","default":{"video_id":"8aGhZQkoFbQ","lang":"en"}}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/YouTubeLatencyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/performance/health":{"get":{"tags":["v1-performance-monitoring (DEPRECATED - use /api/v2/monitoring)","health"],"summary":"Performance Health","description":"Health check endpoint for performance monitoring service.\n\nVerifies that the Chutes API key is configured and the service is ready.\n\n**Returns:**\n```json\n{\n    \"status\": \"healthy\",\n    \"service\": \"performance_monitoring\",\n    \"api_key_configured\": true,\n    \"timestamp\": 1642682400.0\n}\n```\n\n**Status Codes:**\n- **200**: Service is healthy and API key is configured\n- **503**: Service is unhealthy or API key not configured","operationId":"performance_health_api_v1_performance_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PerformanceHealthResponse"}}}}},"deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v1/internal/cache/refresh-arrpv":{"post":{"tags":["internal-cache"],"summary":"Refresh Arrpv","description":"Refresh the creator ARRPV cache table.","operationId":"refresh_arrpv_api_v1_internal_cache_refresh_arrpv_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/briefs":{"get":{"tags":["youtube-v2","youtube-briefs"],"summary":"Get Briefs","description":"Retrieve YouTube briefs with user-specific filtering.\n\nJWT users are scoped to their own portal_user_id.\nAPI keys and admins can query any user.","operationId":"get_briefs_api_v2_youtube_briefs_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Portal user ID","title":"Portal User Id"},"description":"Portal user ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefsListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/briefs/health":{"get":{"tags":["youtube-v2","youtube-briefs","health"],"summary":"Briefs Service Health","description":"Health check endpoint for the YouTube briefs service.\n\nTests database connectivity specifically for brief operations.\n\n**Returns:**\n```json\n{\n    \"status\": \"healthy\",\n    \"service\": \"briefs\",\n    \"database_accessible\": true,\n    \"timestamp\": 1642682400.0\n}\n```\n\n**Status Codes:**\n- **200**: Service is healthy\n- **503**: Service is unhealthy (database issues)","operationId":"briefs_service_health_api_v2_youtube_briefs_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceHealthResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/briefs/aggregated":{"get":{"tags":["youtube-v2","youtube-briefs"],"summary":"Get Aggregated Brief Stats","description":"Retrieve aggregated key statistics across ALL YouTube briefs.\n\nThis endpoint provides comprehensive aggregated statistics including:\n- Total views across all videos from all briefs\n- Total minutes watched across all videos from all briefs\n- Total video count across all briefs\n- Total brief count\n- Cumulative timeseries data (views and minutes by date)\n- Latest data timestamp across all briefs\n\n**Caching:**\n- Response is cached for 18 hours (64,800 seconds)\n- Single cache for all requests (no parameters)\n\n**Note:** This endpoint does NOT include brief-specific information, \nonly aggregated statistics across the entire system.\n\n**Returns:**\nAggregated statistics and cumulative timeseries data across all briefs\n\n**Response Format:**\n```json\n{\n    \"aggregated_stats\": {\n        \"total_views\": 2500000,\n        \"total_minutes_watched\": 125000,\n        \"video_count\": 45,\n        \"brief_count\": 5,\n        \"latest_data_timestamp\": \"2025-01-28T10:30:00Z\"\n    },\n    \"timeseries_data\": {\n        \"views\": {\n            \"2025-01-15\": 25000,\n            \"2025-01-16\": 55000,\n            \"2025-01-17\": 75000\n        },\n        \"minutes_watched\": {\n            \"2025-01-15\": 1250,\n            \"2025-01-16\": 2750,\n            \"2025-01-17\": 3750\n        }\n    }\n}\n```\n\n**Data Strategy:**\n- Aggregates data from ALL briefs in the system\n- For each video, uses most recent timeseries data available\n- Aggregates data across all matched videos from all briefs\n- Returns all available historical data (no date range limits)\n- Timeseries data is cumulative (each date shows running total, not daily values)\n- Includes count of briefs contributing to the aggregation\n- Uses hardcoded static data for minutes watched before 2025-08-13 (converted from hours to minutes)\n\n**Performance:**\n- Uses optimized queries with proper indexes\n- Includes 18-hour response caching for optimal performance\n- Handles large datasets efficiently with single aggregation query\n\n**Error Responses:**\n- **200 with zero values**: No briefs or videos exist (valid response)\n- **500**: Internal server error or database connectivity issues","operationId":"get_aggregated_brief_stats_api_v2_youtube_briefs_aggregated_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AggregatedBriefResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/briefs/{brief_id}":{"get":{"tags":["youtube-v2","youtube-briefs"],"summary":"Get Brief Details","description":"Retrieve detailed information for a specific YouTube brief including aggregated statistics and cumulative timeseries data.\n\nThis endpoint provides comprehensive information about a brief including:\n- All basic brief information (same as /briefs endpoint)\n- Aggregated statistics across all videos ever matched to this brief\n- Cumulative historical timeseries data for views and minutes watched\n\n**Caching:**\n- Response is cached for 12 hours (43,200 seconds)\n- Cache key includes brief_id parameter\n\n**Parameters:**\n- **brief_id**: Brief ID in string format (e.g., \"brief_001\")\n\n**Returns:**\nDetailed brief information with aggregated stats and cumulative timeseries data\n\n**Response Format:**\n```json\n{\n    \"brief\": {\n        \"id_brief\": 1,\n        \"brief_id\": \"brief_001\", \n        \"brief\": \"Brief content description\",\n        \"start_date\": \"2025-01-01\",\n        \"end_date\": \"2025-01-31\",\n        \"format\": \"adRead\",\n        \"boost\": 1.5,\n        \"cap\": 100.0,\n        \"prompt_version\": 1,\n        \"unique_identifier\": null,\n        \"brand\": 1,\n        \"budget\": 1000.0,\n        \"budget_multiplier\": 1.5,\n        \"max_count\": 100,\n        \"status\": \"Live\"\n    },\n    \"aggregated_stats\": {\n        \"total_views\": 1500000,\n        \"total_minutes_watched\": 75000,\n        \"video_count\": 25,\n        \"latest_data_timestamp\": \"2025-01-28T10:30:00Z\"\n    },\n    \"timeseries_data\": {\n        \"views\": {\n            \"2025-01-15\": 10000,\n            \"2025-01-16\": 22000,\n            \"2025-01-17\": 30000\n        },\n        \"minutes_watched\": {\n            \"2025-01-15\": 500,\n            \"2025-01-16\": 1100,\n            \"2025-01-17\": 1500\n        }\n    }\n}\n```\n\n**Data Strategy:**\n- Includes ALL videos historically matched to this brief (not just current/active)\n- For each video, uses most recent timeseries data available\n- Aggregates data across all matched videos\n- Returns all available historical data (no date range limits)\n- Timeseries data is cumulative (each date shows running total, not daily values)\n\n**Error Responses:**\n- **404**: Brief ID not found\n- **200 with empty data**: Brief exists but no videos matched (per BA requirements)\n- **500**: Internal server error or database connectivity issues","operationId":"get_brief_details_api_v2_youtube_briefs__brief_id__get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","description":"Brief ID (string format like 'brief_001') to retrieve details for","title":"Brief Id"},"description":"Brief ID (string format like 'brief_001') to retrieve details for"},{"name":"portal_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Portal user ID for template variable interpolation","title":"Portal User Id"},"description":"Portal user ID for template variable interpolation"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/briefs/{brief_id}/videos":{"get":{"tags":["youtube-v2","youtube-briefs"],"summary":"Get Brief Videos","description":"Retrieve all videos matched to a specific YouTube brief.\n\nThis endpoint returns comprehensive information about all videos matched to a brief including:\n- Complete brief information (all fields from /briefs endpoint)\n- Video details: bitcast_video_id, title, published_at, duration, url\n- Account information: bitcast_account_id, account_title\n- Engagement metrics: views, likes, shares (from latest yt_video_run_metrics)\n- Brief budget information with CPM-based spend estimation:\n    - creator_budget: Brief budget amount allocated for creators\n    - budget_multiplier: Controls rate of budget drawdown (defaults to 1 if null)\n    - remaining_budget: creator_budget - (total_spend / budget_multiplier)\n    - usd_spend: Total USD spent (creator_budget - remaining_budget)\n\n**Caching:**\n- Response is cached for 12 hours (43,200 seconds)\n- Cache key includes brief_id parameter\n\n**Path Parameters:**\n- **brief_id**: Brief ID in string format (e.g., \"brief_001\")\n\n**Returns:**\nComprehensive brief and video information\n\n**Response Format:**\n```json\n{\n    \"brief_info\": {\n        \"id_brief\": 1,\n        \"brief_id\": \"brief_001\",\n        \"brief\": \"Brief content description\",\n        \"start_date\": \"2025-01-01\",\n        \"end_date\": \"2025-01-31\",\n        \"format\": \"adRead\",\n        \"boost\": 1.5,\n        \"cap\": 100.0,\n        \"prompt_version\": 1,\n        \"unique_identifier\": null,\n        \"brand\": 1,\n        \"max_count\": 100,\n        \"status\": \"Live\",\n        \"creator_budget\": 1000.0,\n        \"budget_multiplier\": 1.2,\n        \"remaining_budget\": 916.67,\n        \"usd_spend\": 83.33\n    },\n    \"videos\": [\n        {\n            \"bitcast_video_id\": \"vid_abc123\",\n            \"title\": \"Sample Video\",\n            \"published_at\": \"2025-01-15T10:30:00Z\",\n            \"duration\": \"PT10M30S\",\n            \"url\": \"https://youtube.com/watch?v=abc123\",\n            \"bitcast_account_id\": \"acc_xyz789\",\n            \"account_title\": \"Creator Channel Name\",\n            \"views\": 125000,\n            \"likes\": 3500,\n            \"shares\": 450\n        }\n    ],\n    \"total_count\": 1\n}\n```\n\n**Budget Calculation (CPM-Based):**\nThe spend estimation uses a creator-specific CPM approach:\n\n1. **Creator ARRPV (Average Red Revenue Per View):**\n   - For each creator, calculate: ARRPV = total_red_revenue / total_views (past 3 months)\n   - Uses actual YouTube red revenue data from yt_video_run_metrics\n   - Falls back to default ARRPV ($0.000398/view) for creators without 3-month history\n\n2. **Per-Video Estimation:**\n   - ERR (Estimated Red Revenue) = ARRPV × video_views\n   - estimated_spend = (400 × √ERR) / (1 + 0.1 × √ERR)\n\n3. **Total Spend Calculation:**\n   - total_spend = sum of all video estimated_spend values\n   - remaining_budget = creator_budget - (total_spend / budget_multiplier)\n   - usd_spend = creator_budget - remaining_budget\n\n4. **Budget Multiplier:**\n   - Controls rate of budget drawdown\n   - If budget_multiplier is 1.2, budget draws down 20% slower than actual spend\n   - Example: creator_budget=1000, multiplier=1.2, spend=100 → \n     remaining = 1000 - (100/1.2) = 916.67, usd_spend = 83.33\n   - Defaults to 1.0 if null in database\n\n5. **Edge Cases:**\n   - Videos with 0 views: estimated_spend = $0\n   - Creators without history: use default ARRPV = $0.000398/view\n   - Negative red revenue: treated as $0\n\n**Data Strategy:**\n- Returns ALL videos historically matched to this brief\n- Ordered by published_at descending (most recent first)\n- Includes videos regardless of current brief status (Live/Complete/Upcoming)\n- Uses metrics from the validator run when the video matched the brief\n\n**Error Responses:**\n- **404**: Brief ID not found\n- **500**: Internal server error or database connectivity issues","operationId":"get_brief_videos_api_v2_youtube_briefs__brief_id__videos_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","description":"Brief ID to get videos for","title":"Brief Id"},"description":"Brief ID to get videos for"},{"name":"portal_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Portal user ID for template variable interpolation","title":"Portal User Id"},"description":"Portal user ID for template variable interpolation"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefVideosResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/videos/health":{"get":{"tags":["youtube-v2","youtube-videos","health"],"summary":"Videos Service Health","description":"Health check endpoint for the YouTube videos service.\n\nTests database connectivity specifically for video operations.\n\n**Returns:**\n```json\n{\n    \"service\": \"videos\",\n    \"status\": \"healthy\",\n    \"timestamp\": \"2025-01-29T12:00:00Z\",\n    \"database\": {\n        \"connected\": true,\n        \"response_time_ms\": 45\n    }\n}\n```\n\n**Status Codes:**\n- **200**: Service is healthy\n- **503**: Service is unhealthy (database issues)","operationId":"videos_service_health_api_v2_youtube_videos_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoHealthResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/videos/{bitcast_video_id}":{"get":{"tags":["youtube-v2","youtube-videos"],"summary":"Get Single Video","description":"Retrieve a single YouTube video with detailed calculated metrics.\n\nThis endpoint returns comprehensive details for a single video including \ncalculated earnings metrics, daily time-series data, and additional metadata.\n\n**Path Parameters:**\n- **bitcast_video_id**: Unique Bitcast video identifier\n\n**Query Parameters:**\n- **portal_user_id**: Optional. When supplied, the user's platform and\n  agency margins are applied to all earnings fields. Omit for gross figures.\n\n**Returns:**\nSingle video with complete calculated metrics and daily time-series data\n\n**Calculated Fields:**\n- **brief_id**: Brief ID if video is matched to a brief, null otherwise (handles multiple brief matches by selecting lowest brief ID)\n- **boost**: Brief boost multiplier from matched brief, null if no brief matched\n- **format**: Brief format (e.g., 'adRead', 'dedicated') from matched brief, null if no brief matched\n- **daily_earnings_usd**: Average usd_target from validator runs in past 24 hours (with weight corrections)\n- **daily_earnings_alpha**: Average alpha_target from validator runs in past 24 hours (with weight corrections)\n- **usd_total**: Sum of daily averages for usd_target across entire video history (with weight corrections)\n- **alpha_total**: Sum of daily averages for alpha_target across entire video history (with weight corrections)\n- **daily_metrics**: Time-series array of daily earnings by date\n- **views**: Latest view count from video run metrics\n- **estimated_minutes_watched**: Latest watch time estimate from video run metrics\n- **estimated_red_partner_revenue**: Latest partner revenue estimate from video run metrics\n- **status**: Video monetization status:\n    - \"Earning\": Video matched to brief and published ≤ 3 weeks ago\n    - \"Complete\": Video matched to brief and published > 3 weeks ago  \n    - \"Non-monotized\": Video not matched to any brief\n\n**Response Format:**\n```json\n{\n    \"title\": \"Sample Video Title\",\n    \"views\": 5000,\n    \"published_at\": \"2025-01-15T10:30:00Z\",\n    \"daily_earnings_usd\": 25.50,\n    \"daily_earnings_alpha\": 200.75,\n    \"usd_total\": 150.75,\n    \"alpha_total\": 1250.25,\n    \"daily_metrics\": [\n        {\n            \"date\": \"2025-01-15\",\n            \"total_usd\": 10.50,\n            \"total_alpha\": 100.25\n        },\n        {\n            \"date\": \"2025-01-16\", \n            \"total_usd\": 15.25,\n            \"total_alpha\": 110.50\n        }\n    ],\n    \"brief_id\": \"brief_001\",\n    \"format\": \"adRead\",\n    \"boost\": 1.5,\n    \"status\": \"Earning\"\n}\n```\n\n**Caching:**\n- Response is cached for 30 minutes (1800 seconds)\n- Cache key includes bitcast_video_id and portal_user_id, so margined and\n  anonymous (and per-user) responses never share a cache entry\n\n**Status Codes:**\n- **200**: Video found and metrics calculated successfully\n- **404**: Video not found\n- **500**: Database error or calculation error","operationId":"get_single_video_api_v2_youtube_videos__bitcast_video_id__get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"bitcast_video_id","in":"path","required":true,"schema":{"type":"string","description":"The Bitcast unique identifier for the video.","title":"Bitcast Video Id"},"description":"The Bitcast unique identifier for the video."},{"name":"portal_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Portal user whose platform/agency margin should be applied to the returned earnings. Omit for gross (unmargined) figures.","title":"Portal User Id"},"description":"Portal user whose platform/agency margin should be applied to the returned earnings. Omit for gross (unmargined) figures."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SingleVideoResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/users/health":{"get":{"tags":["youtube-v2","youtube-users","health"],"summary":"Users Service Health","description":"Health check endpoint for the users service.\n\nTests database connectivity specifically for user operations.\n\n**Returns:**\n```json\n{\n    \"status\": \"healthy\",\n    \"service\": \"users\",\n    \"database_accessible\": true,\n    \"timestamp\": 1642682400.0\n}\n```\n\n**Status Codes:**\n- **200**: Service is healthy\n- **503**: Service is unhealthy (database issues)","operationId":"users_service_health_api_v2_youtube_users_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceHealthResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/users":{"get":{"tags":["youtube-v2","youtube-users"],"summary":"Get User Details","description":"Retrieve user details by email or portal_user_id.\n\nJWT users can only access their own data. API keys and admins can query any user.","operationId":"get_user_details_api_v2_youtube_users_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"email","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"}},{"name":"portal_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Portal User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDetailsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["youtube-v2","youtube-users"],"summary":"Create User","description":"Create a new user.\n\nCreates a new user with the provided email address. All new users have\nmining_coldkey set to \"no-code\". A unique referral code is automatically\ngenerated from the user's email.\n\n**Parameters:**\n- **email**: The email address for the new user\n\n**Returns:**\n- Complete user data including empty YouTube credentials list and referral code\n\n**Request Format:**\n```json\n{\n    \"email\": \"user@example.com\"\n}\n```\n\n**Response Format:**\n```json\n{\n    \"id_portal_user\": 1,\n    \"email\": \"user@example.com\",\n    \"mining_coldkey\": \"no-code\",\n    \"payment_coldkey\": \"\",\n    \"referral_code\": \"f6g7h8i9j0\",\n    \"created_at\": \"2025-01-29T10:30:00Z\",\n    \"updated_at\": \"2025-01-29T10:30:00Z\",\n    \"yt_credentials\": []\n}\n```\n\n**Business Logic:**\n- mining_coldkey is always set to \"no-code\" for new users\n- payment_coldkey is set to empty string\n- platform_margin defaults to 0.0500 (5% margin)\n- referral_code is automatically generated from email (first 10 chars of SHA256 hash)\n- yt_credentials list is empty for new users\n- Referral tracking is now handled via the waitlist-details endpoint\n\n**Errors:**\n- **409**: User already exists with the provided email\n- **400**: Invalid request data (invalid email format)\n- **500**: Database error occurred","operationId":"create_user_api_v2_youtube_users_post","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/users/list":{"get":{"tags":["youtube-v2","youtube-users"],"summary":"Get All Users","description":"Retrieve the full list of all users with ID and email.\n\n**Returns:**\nList of all users showing only portal_user_id and email for each user.\n\n**Response Format:**\n```json\n{\n    \"users\": [\n        {\n            \"id_portal_user\": 1,\n            \"email\": \"user1@example.com\"\n        },\n        {\n            \"id_portal_user\": 2, \n            \"email\": \"user2@example.com\"\n        }\n    ],\n    \"total_count\": 2\n}\n```\n\n**Use Case:**\nThis endpoint is useful for Q2.1 scenarios where you need to:\n- Get a user's ID via their email to fetch videos\n- Browse all available users in the system\n- Get a complete user directory for administrative purposes\n\n**Performance:** \nOptimized to return only ID and email fields for faster response times.","operationId":"get_all_users_api_v2_youtube_users_list_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserListResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/users/earnings":{"get":{"tags":["youtube-v2","youtube-users"],"summary":"Get All Yt Accounts Earnings","description":"Retrieve earnings for ALL YouTube accounts with weight corrections applied.\n\nThis endpoint returns all YouTube accounts that have earned money, including:\n- **Linked accounts**: Accounts connected to portal users via yt_credentials\n- **Unlinked accounts**: Accounts with earnings but not connected to any portal user\n\n**Earnings Calculation:**\n- Uses weight corrections from the `weight_corrections` table\n- `scaling_factor = 1.0`: Full earnings counted\n- `scaling_factor = 0.0`: Earnings zeroed out\n- Missing weight correction defaults to `scaling_factor = 1.0`\n- Daily averages are calculated per video, then summed across all days\n\n**Caching:**\n- Response is cached for 1 hour (3,600 seconds)\n\n**Response Format:**\n```json\n{\n    \"accounts\": [\n        {\n            \"yt_account_id\": 11,\n            \"bitcast_account_id\": \"bitcast_580cc257\",\n            \"account_title\": \"THE EARLY STAGE INVESTOR\",\n            \"portal_user_id\": 47,\n            \"portal_user_email\": \"eliot.s.molling@gmail.com\",\n            \"is_linked\": true,\n            \"usd_total\": 32164.33,\n            \"alpha_total\": 6720.99\n        },\n        {\n            \"yt_account_id\": 20,\n            \"bitcast_account_id\": \"bitcast_a4f1cf2e\",\n            \"account_title\": \"Algo Trade\",\n            \"portal_user_id\": null,\n            \"portal_user_email\": null,\n            \"is_linked\": false,\n            \"usd_total\": 13872.67,\n            \"alpha_total\": 2271.99\n        }\n    ],\n    \"totals\": {\n        \"total_usd\": 226802.11,\n        \"total_alpha\": 43391.25,\n        \"linked_accounts_count\": 17,\n        \"unlinked_accounts_count\": 11,\n        \"linked_usd\": 198172.22,\n        \"unlinked_usd\": 28629.90\n    },\n    \"total_count\": 28\n}\n```\n\n**Use Cases:**\n- Get complete picture of all YouTube earnings across the platform\n- Identify accounts that need to be linked to portal users\n- Reconcile earnings between API and database totals\n\n**Performance:**\n- Uses optimized CTEs with weight corrections\n- Response cached for 1 hour","operationId":"get_all_yt_accounts_earnings_api_v2_youtube_users_earnings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/YtAllAccountsEarningsResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/users/distribution":{"get":{"tags":["youtube-v2","youtube-users"],"summary":"Get Yt Distribution","description":"Get YouTube earnings distribution with finalized vs pending breakdown per account.\n\n**Earnings Breakdown:**\n- **Finalized (weight=0)**: Sum of usd_target where weight = 0\n- **Pending (weight>0)**: Sum of latest usd_target per video from latest validatorRun where weight > 0\n- **Total**: finalized + pending\n\n**Filters:**\n- Only non-blacklisted accounts (blacklisted = 0)\n\n**Query Parameters:**\n- **timeframe**: `'30d'` (last 30 days) or `'all'` (all-time, default)\n\n**Caching:** 1 hour per timeframe","operationId":"get_yt_distribution_api_v2_youtube_users_distribution_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"timeframe","in":"query","required":false,"schema":{"type":"string","description":"Timeframe filter: '30d' for last 30 days, 'all' for all-time","default":"all","title":"Timeframe"},"description":"Timeframe filter: '30d' for last 30 days, 'all' for all-time"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/YtDistributionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/users/{portal_user_id}/access":{"patch":{"tags":["youtube-v2","youtube-users"],"summary":"Update User Access","description":"Approve or revoke user access.\n\nSets the access flag for a user. True = approved, False = revoked/waitlisted.","operationId":"update_user_access_api_v2_youtube_users__portal_user_id__access_patch","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","minimum":1,"description":"Portal user ID","title":"Portal User Id"},"description":"Portal user ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAccessRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAccessResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/users/{portal_user_id}/archive":{"patch":{"tags":["youtube-v2","youtube-users"],"summary":"Update User Archive","description":"Archive or unarchive a user.\n\nSets the archived flag for a user. True = archived, False = unarchived.","operationId":"update_user_archive_api_v2_youtube_users__portal_user_id__archive_patch","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","minimum":1,"description":"Portal user ID","title":"Portal User Id"},"description":"Portal user ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateArchiveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateArchiveResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/users/{portal_user_id}/inactive":{"patch":{"tags":["youtube-v2","youtube-users"],"summary":"Update User Inactive","description":"Mark a creator as inactive or active.\n\nSets the inactive flag for a creator. True = inactive, False = active.","operationId":"update_user_inactive_api_v2_youtube_users__portal_user_id__inactive_patch","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","minimum":1,"description":"Portal user ID","title":"Portal User Id"},"description":"Portal user ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateInactiveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateInactiveResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/users/{portal_user_id}/payment-coldkey":{"put":{"tags":["youtube-v2","youtube-users"],"summary":"Update Payment Coldkey","description":"Add or update the payment coldkey for a user.\n\nSupports both API key and JWT auth. JWT users are scoped to their own ID.\n\n**Parameters:**\n- **portal_user_id**: Portal user ID to update\n- **payment_coldkey**: Payment coldkey to set","operationId":"update_payment_coldkey_api_v2_youtube_users__portal_user_id__payment_coldkey_put","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","title":"Portal User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePaymentColdkeyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePaymentColdkeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/users/{portal_user_id}/waitlist-details":{"post":{"tags":["youtube-v2","youtube-users"],"summary":"Create User Waitlist Details","description":"Create or update waitlist details for a user.\n\nStores social media URLs/handles (YouTube and X/Twitter) and referral information \nfor users on the waitlist. Uses UPSERT behavior - creates a new record if none exists, \nor updates the existing record.\n\n**Path Parameters:**\n- **portal_user_id**: The portal user ID (must be positive integer)\n\n**Request Body:**\n- **youtube**: YouTube URL or handle (optional, max 255 characters)\n    - Accepts full URLs: https://www.youtube.com/@channel, https://youtube.com/@channel\n    - Accepts simple handles: @channel\n- **x**: X/Twitter URL or handle (optional, max 255 characters)\n    - Accepts full URLs: https://x.com/handle, https://www.x.com/handle\n    - Accepts simple handles: @handle\n- **referred_by**: Referral code of the user who referred this user (optional, max 10 characters)\n\n**Validation:**\n- All fields are optional (can provide none, one, or multiple)\n- YouTube URLs must match pattern: (https?://)?(www\\.)?(youtube\\.com|youtu\\.be)/.+\n- X URLs must match pattern: (https?://)?(www\\.)?x\\.com/.+\n- Simple handles allow only: alphanumeric, @, _, -, and spaces\n- referred_by is not validated against existing referral codes (stored as-is)\n\n**Example Request:**\n```json\n{\n    \"youtube\": \"https://www.youtube.com/@example_channel\",\n    \"x\": \"https://x.com/example_handle\",\n    \"referred_by\": \"a1b2c3d4e5\"\n}\n```\n\n**Example Response (201 Created):**\n```json\n{\n    \"id_user_waitlist_details\": 1,\n    \"portal_user_id\": 1,\n    \"youtube\": \"https://www.youtube.com/@example_channel\",\n    \"x\": \"https://x.com/example_handle\",\n    \"created_at\": \"2025-10-10T10:00:00Z\",\n    \"updated_at\": \"2025-10-10T10:00:00Z\"\n}\n```\n\n**Status Codes:**\n- **201**: Waitlist details created or updated successfully\n- **422**: Invalid request data (Pydantic validation failed)\n- **404**: User not found (foreign key constraint violation)\n- **500**: Database error occurred\n\n**Notes:**\n- If waitlist details already exist for this user, they will be updated (UPSERT)\n- User must exist in portal_user table (enforced by foreign key)\n- Empty/whitespace-only URLs/handles are treated as null\n- This endpoint now handles referral tracking (updates portal_user.referred_by)","operationId":"create_user_waitlist_details_api_v2_youtube_users__portal_user_id__waitlist_details_post","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","minimum":1,"description":"Portal user ID","title":"Portal User Id"},"description":"Portal user ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWaitlistDetailsRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WaitlistDetailsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/users/{portal_user_id}/notification-preferences":{"patch":{"tags":["youtube-v2","youtube-users"],"summary":"Update Notification Preferences","description":"Update notification preferences for a user.\n\nSupports partial updates - you can update just email, just telegram, or both.\nUses PATCH method for partial resource updates (REST best practice).\n\n**Parameters:**\n- **portal_user_id**: Portal user ID to update (path parameter)\n- **notify_email**: Email notification preference (optional, boolean)\n- **notify_telegram**: Telegram notification preference (optional, boolean)\n\n**Example Request (update both):**\n```json\n{\n    \"notify_email\": true,\n    \"notify_telegram\": false\n}\n```\n\n**Example Request (update only email):**\n```json\n{\n    \"notify_email\": false\n}\n```\n\n**Example Response:**\n```json\n{\n    \"id_portal_user\": 1,\n    \"notify_email\": false,\n    \"notify_telegram\": true\n}\n```\n\n**Status Codes:**\n- **200**: Notification preferences updated successfully\n- **400**: Invalid request (no fields provided or invalid data)\n- **404**: User not found\n- **500**: Database error occurred","operationId":"update_notification_preferences_api_v2_youtube_users__portal_user_id__notification_preferences_patch","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","minimum":1,"description":"Portal user ID","title":"Portal User Id"},"description":"Portal user ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateNotificationPreferencesRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/users/{portal_user_id}/telegram-chat-id":{"put":{"tags":["youtube-v2","youtube-users"],"summary":"Update Telegram Chat Id","description":"Update the Telegram chat ID for a user.\n\nThis endpoint allows updating or clearing the Telegram chat ID associated with a user account.\nThe Telegram chat ID is used for sending notifications via Telegram.\n\n**Path Parameters:**\n- **portal_user_id**: The portal user ID (must be positive integer)\n\n**Request Body:**\n- **telegram_chat_id**: Telegram chat ID to set (optional, null to clear)\n\n**Example Request (set chat ID):**\n```json\n{\n    \"telegram_chat_id\": \"123456789\"\n}\n```\n\n**Example Request (clear chat ID):**\n```json\n{\n    \"telegram_chat_id\": null\n}\n```\n\n**Example Response:**\n```json\n{\n    \"id_portal_user\": 1,\n    \"telegram_chat_id\": \"123456789\",\n    \"updated_at\": \"2025-11-18T10:30:00Z\"\n}\n```\n\n**Status Codes:**\n- **200**: Telegram chat ID updated successfully\n- **404**: User not found\n- **500**: Database error occurred\n\n**Notes:**\n- Empty strings are treated as null (chat ID is cleared)\n- Whitespace is automatically trimmed from the chat ID\n- User must exist in portal_user table","operationId":"update_telegram_chat_id_api_v2_youtube_users__portal_user_id__telegram_chat_id_put","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","minimum":1,"description":"Portal user ID","title":"Portal User Id"},"description":"Portal user ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTelegramChatIdRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTelegramChatIdResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/users/{portal_user_id}/videos":{"get":{"tags":["youtube-v2","youtube-users"],"summary":"Get User Videos","description":"Retrieve videos for a specific YouTube user with calculated metrics.\n\nThis endpoint returns videos belonging to a specific portal user, filtered by publication date,\nwith complex calculated metrics including brief matching data, earnings, and performance metrics.\n\n**Caching:**\n- Response is cached for 30 minutes (1800 seconds)\n- Cache key includes portal_user_id and date_lookback parameters\n\n**Path Parameters:**\n- **portal_user_id**: The portal user ID to retrieve videos for\n\n**Query Parameters:**\n- **date_lookback**: Days to look back for video selection (default: 30, min: 1, max: 365)\n\n**Returns:**\n- List of user videos with calculated metrics\n- Only includes videos with privacy_status = 'public'\n- Date filter applies only to video selection, not metric calculations\n\n**Calculated Fields:**\n- **brief_id**: Brief ID if video matched to brief (null otherwise)\n- **daily_earnings**: Average usd_target from validator runs in past 24 hours from request time (with weight corrections applied)\n- **usd_total**: Sum of daily averages for usd_target across entire video history (with weight corrections applied)\n- **alpha_total**: Sum of daily averages for alpha_target across entire video history (with weight corrections applied)\n- **cpm**: Cost per mille (CPM) calculated as (usd_total / latest_views) * 1000\n- **status**: Video monetization status:\n    - \"Earning\": Video matched to brief and published ≤ 3 weeks ago\n    - \"Complete\": Video matched to brief and published > 3 weeks ago  \n    - \"Non-monotized\": Video not matched to any brief\n\n**Errors:**\n- **404**: Portal user not found\n- **500**: Database error occurred\n- **503**: Service temporarily unavailable","operationId":"get_user_videos_api_v2_youtube_users__portal_user_id__videos_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","description":"Portal user ID to retrieve videos for","title":"Portal User Id"},"description":"Portal user ID to retrieve videos for"},{"name":"date_lookback","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"Number of days to look back for video selection (based on published_at date)","default":30,"title":"Date Lookback"},"description":"Number of days to look back for video selection (based on published_at date)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/users/{portal_user_id}/account/stats":{"get":{"tags":["youtube-v2","youtube-users"],"summary":"Get Account Stats","description":"Retrieve account-level aggregated statistics across all user videos with brief matches.\n\nThis endpoint returns aggregated metrics for an entire account, summing data across\nall videos that exist in the video_matched_to_brief table (i.e., videos with brief matches).\n\n**Caching:**\n- Response is cached for 30 minutes (1800 seconds)\n- Cache key includes portal_user_id and lookback_days parameters\n\n**Path Parameters:**\n- **portal_user_id**: Portal user ID for the account\n\n**Query Parameters:**\n- **lookback_days**: Days to look back for totals calculation (default: unlimited, min: 1)\n\n**Returns:**\nAccount-level aggregated statistics with calculated metrics\n\n**Calculated Fields:**\n- **daily_earnings**: Sum of per-video averages from past 24 hours (with weight corrections)\n- **usd_total**: Sum of daily averages across all account videos within lookback period (with weight corrections)  \n- **alpha_total**: Sum of daily averages across all account videos within lookback period (with weight corrections)\n- **cpm**: Cost per mille calculated as (total_usd_targets / total_views * 1000) over lookback period\n- **outstanding_balance_alpha**: Outstanding balance in Alpha tokens from payments with status 'staged' or 'failed'\n- **account_title**: Account title from yt_account.title field\n\n**Scope:**\n- Only includes videos that exist in video_matched_to_brief table\n- Applies same weight corrections as individual video APIs\n- Returns zero values for accounts with no videos or missing data (200 OK)\n\n**Performance:**\n- Response cached for 5 minutes\n- Target response time: < 500ms for typical account sizes","operationId":"get_account_stats_api_v2_youtube_users__portal_user_id__account_stats_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","description":"Portal user ID for account stats retrieval","title":"Portal User Id"},"description":"Portal user ID for account stats retrieval"},{"name":"lookback_days","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Number of days to look back for totals calculation (default: unlimited)","title":"Lookback Days"},"description":"Number of days to look back for totals calculation (default: unlimited)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountStatsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/users/{portal_user_id}/payments":{"get":{"tags":["youtube-v2","youtube-users","user-payments"],"summary":"Get User Payments","description":"Get completed/confirmed payments for a user, grouped by reward_date.\n\nReturns daily aggregated payment totals (USD and Alpha).\nFast query — single table, no heavy joins.","operationId":"get_user_payments_api_v2_youtube_users__portal_user_id__payments_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"path","required":true,"schema":{"type":"integer","description":"Portal user ID to get payments for","title":"Portal User Id"},"description":"Portal user ID to get payments for"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"},"title":"Response Get User Payments Api V2 Youtube Users  Portal User Id  Payments Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/credentials/credentials/upload":{"post":{"tags":["youtube-v2","youtube-credentials"],"summary":"Upload YouTube JSON credential file","description":"Upload and validate YouTube API credentials in JSON format. JSON files are automatically converted to Google OAuth2 Credentials objects using system-provided client credentials.","operationId":"upload_credentials_api_v2_youtube_credentials_credentials_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_credentials_api_v2_youtube_credentials_credentials_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialUploadResponse"}}}},"400":{"description":"Invalid file or validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialUploadError"}}}},"404":{"description":"Portal user not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialUploadError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialUploadError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/credentials/upload":{"post":{"tags":["youtube-v2","youtube-credentials"],"summary":"Upload YouTube credentials (JWT auth)","description":"Upload YouTube credentials using portal JWT authentication. User ID is extracted from the JWT — no need to pass it in the form.","operationId":"upload_credentials_jwt_api_v2_youtube_credentials_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_credentials_jwt_api_v2_youtube_credentials_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialUploadResponse"}}}},"400":{"description":"Invalid file or validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialUploadError"}}}},"401":{"description":"JWT authentication required"},"404":{"description":"Portal user not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialUploadError"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialUploadError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/credentials/health":{"get":{"tags":["youtube-v2","youtube-credentials"],"summary":"Credential system health check","description":"Check if the credential management system is operational","operationId":"credential_system_health_api_v2_youtube_credentials_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialHealthResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/credentials/access-tokens":{"get":{"tags":["youtube-v2","youtube-credentials"],"summary":"Get YouTube access tokens for miner usage","description":"Decrypts stored refresh tokens, exchanges them for fresh access tokens server-side, and returns only the short-lived access tokens. Requires API key with permission level 2 or higher.","operationId":"get_access_tokens_for_miner_api_v2_youtube_credentials_access_tokens_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MinerAccessTokensResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/notifications/health":{"get":{"tags":["youtube-v2","youtube-notifications","health"],"summary":"Notifications Health","description":"Health check endpoint for the notifications service.\n\nTests database connectivity specifically for notification operations.\n\n**Returns:**\n```json\n{\n    \"status\": \"healthy\",\n    \"service\": \"notifications\",\n    \"database_accessible\": true,\n    \"timestamp\": 1642682400.0\n}\n```\n\n**Status Codes:**\n- **200**: Service is healthy\n- **503**: Service is unhealthy (database issues)","operationId":"notifications_health_api_v2_youtube_notifications_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceHealthResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/notifications/queue/unprocessed":{"get":{"tags":["youtube-v2","youtube-notifications"],"summary":"Get Unprocessed Notifications","description":"Retrieve unprocessed notifications from the queue.\n\nReturns notifications where processed=0, ordered by created_at DESC (newest first).\n\n**Query Parameters:**\n- **platform**: Optional filter by platform (portal, telegram, or email)\n\n**Example Response:**\n```json\n{\n    \"notifications\": [\n        {\n            \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n            \"platform\": \"email\",\n            \"portal_user_ids\": [1, 2, 3],\n            \"message\": \"Your payment has been processed\",\n            \"redirect_link\": \"https://portal.example.com/payments\",\n            \"processed\": false,\n            \"created_at\": \"2025-11-07T10:00:00Z\"\n        }\n    ],\n    \"total_count\": 1\n}\n```\n\n**Use Case:**\nClient fetches unprocessed notifications, processes them (sends emails/telegrams/etc),\nthen logs results using POST /notifications/log/bulk endpoint.\n\n**Status Codes:**\n- **200**: Successfully retrieved notifications (may be empty array)\n- **500**: Database error occurred","operationId":"get_unprocessed_notifications_api_v2_youtube_notifications_queue_unprocessed_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"platform","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/NotificationPlatform"},{"type":"null"}],"description":"Filter by platform (portal/telegram/email)","title":"Platform"},"description":"Filter by platform (portal/telegram/email)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnprocessedQueueResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/notifications/queue":{"post":{"tags":["youtube-v2","youtube-notifications"],"summary":"Create Notification Queue Item","description":"**⚠️ TEMPORARY DEV/TESTING ENDPOINT ⚠️**\n\nCreate a real notification in the queue for development/testing purposes.\n\nThis endpoint creates actual notifications without requiring business events\n(like payment processing or brief creation). This enables independent development\nand testing of notification delivery systems (email/telegram/portal senders).\n\n**Example Request:**\n```json\n{\n    \"platform\": \"email\",\n    \"portal_user_ids\": [1, 2, 3],\n    \"message\": \"Test notification - payment processed\",\n    \"redirect_link\": \"https://portal.example.com/payments\"\n}\n```\n\n**Example Response:**\n```json\n{\n    \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"platform\": \"email\",\n    \"portal_user_ids\": [1, 2, 3],\n    \"message\": \"Test notification - payment processed\",\n    \"redirect_link\": \"https://portal.example.com/payments\",\n    \"processed\": false,\n    \"created_at\": \"2025-11-25T10:00:00Z\"\n}\n```\n\n**Status Codes:**\n- **201**: Notification created successfully\n- **400**: Invalid request data (e.g., invalid user IDs)\n- **500**: Database error occurred\n\n**Workflow:**\n1. Create test notification using this endpoint\n2. Fetch it using GET /notifications/queue/unprocessed\n3. Process it (send emails/telegrams/etc)\n4. Log results using POST /notifications/log/bulk","operationId":"create_notification_queue_item_api_v2_youtube_notifications_queue_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNotificationQueueRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNotificationQueueResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/notifications/log/bulk":{"post":{"tags":["youtube-v2","youtube-notifications"],"summary":"Create Notification Logs Bulk","description":"Create multiple notification delivery log entries in one call.\n\nRecords delivery status for multiple users for the same notification.\nAutomatically marks queue as processed when ALL users from portal_user_ids have log entries.\n\n**Use Case:** Process a notification batch for multiple users in a single API call\ninstead of making individual calls per user (much more efficient).\n\n**Example Request:**\n```json\n{\n    \"logs\": [\n        {\n            \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n            \"user_id\": 1,\n            \"success\": true,\n            \"error_msg\": null\n        },\n        {\n            \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n            \"user_id\": 2,\n            \"success\": false,\n            \"error_msg\": \"Email bounced\"\n        },\n        {\n            \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n            \"user_id\": 3,\n            \"success\": true,\n            \"error_msg\": null\n        }\n    ]\n}\n```\n\n**Example Response:**\n```json\n{\n    \"logs\": [\n        {\n            \"id_notification_log\": 123,\n            \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n            \"user_id\": 1,\n            \"success\": true,\n            \"error_msg\": null,\n            \"timestamp\": \"2025-11-07T10:05:00Z\"\n        },\n        {\n            \"id_notification_log\": 124,\n            \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n            \"user_id\": 2,\n            \"success\": false,\n            \"error_msg\": \"Email bounced\",\n            \"timestamp\": \"2025-11-07T10:05:00Z\"\n        },\n        {\n            \"id_notification_log\": 125,\n            \"notification_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n            \"user_id\": 3,\n            \"success\": true,\n            \"error_msg\": null,\n            \"timestamp\": \"2025-11-07T10:05:00Z\"\n        }\n    ],\n    \"total_created\": 3,\n    \"queue_processed\": true,\n    \"queue_fully_logged\": true\n}\n```\n\n**Status Codes:**\n- **201**: Log entries created successfully\n- **404**: Notification ID or user ID not found\n- **400**: Invalid request data or empty logs array\n- **500**: Database error occurred (all or nothing - transaction rolled back)\n\n**Processing Logic:**\n1. Validates all log entries\n2. Inserts all logs in a single transaction\n3. Counts total users in queue's portal_user_ids JSON array\n4. Counts total log entries for this notification_id (including newly inserted)\n5. If counts match, automatically marks queue.processed = 1\n6. Returns all created logs with queue status\n\n**Performance:** One API call can log results for 100+ users efficiently.","operationId":"create_notification_logs_bulk_api_v2_youtube_notifications_log_bulk_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkCreateNotificationLogRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkNotificationLogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/validation/script":{"post":{"tags":["youtube-v2","youtube-validation"],"summary":"Check Script","description":"Check YouTube script content against a brief using 3 concurrent calls to bitcast internal API.\n\nThis endpoint makes 3 concurrent evaluations and returns the most pessimistic result\nto account for LLM non-determinism. If any evaluation returns false, the overall \nresult is false (fail-safe approach).\n\n**Parameters:**\n- **brief_id**: Brief identifier to lookup in database\n- **transcript**: Video transcript content (max 70,000 characters)  \n- **description**: YouTube video description (max 10,000 characters)\n- **duration**: Video duration (format: \"00:08:27\")\n\n**Returns:**\n- Script check result with match status, reasoning, and metadata\n\n**Response Format:**\n```json\n{\n    \"brief_id\": \"test-brief-123\",\n    \"match\": true,\n    \"reasoning\": \"Detailed checking reasoning...\"\n}\n```\n\n**Errors:**\n- **400**: Validation error (input too long, missing fields, invalid duration format)\n- **500**: Internal server error or bitcast API unavailable","operationId":"check_script_api_v2_youtube_validation_script_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScriptCheckerRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScriptCheckerResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/validation/health":{"get":{"tags":["youtube-v2","youtube-validation","health"],"summary":"Validation Service Health","description":"Health check endpoint for the YouTube validation service.\n\nTests script checker service connectivity.\n\n**Returns:**\n```json\n{\n    \"status\": \"healthy\",\n    \"bitcast_api_available\": true,\n    \"response_time_ms\": 45.2,\n    \"error\": null\n}\n```\n\n**Status Codes:**\n- **200**: Service is healthy\n- **503**: Service is unhealthy","operationId":"validation_service_health_api_v2_youtube_validation_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScriptCheckerHealthResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/daily-summary":{"get":{"tags":["youtube-v2","youtube-daily-summary"],"summary":"Get Youtube Daily Summary","description":"Retrieve YouTube platform-wide daily summary metrics.\n\nThis endpoint provides aggregated daily metrics across the entire YouTube platform including:\n- Videos published per day\n- Total views per day\n- Total minutes watched per day\n- Engagement metrics (shares, likes)\n- Earnings (USD and Alpha)\n- Account metrics (connected accounts, total subscribers)\n\n**Caching:**\n- Response is cached for 1 hour (3,600 seconds)\n- Cache key includes start_date, end_date, and limit parameters\n\n**Query Parameters:**\n- **start_date**: Optional start date filter (YYYY-MM-DD, inclusive)\n- **end_date**: Optional end date filter (YYYY-MM-DD, inclusive)\n- **limit**: Optional maximum number of days to return (most recent first)\n\n**Returns:**\nList of daily summaries ordered by date (most recent first)\n\n**Response Format:**\n```json\n{\n    \"daily_summaries\": [\n        {\n            \"date\": \"2026-01-26\",\n            \"videos_published\": 15,\n            \"views\": 125000,\n            \"minutes_watched\": 6250,\n            \"shares\": 450,\n            \"likes\": 3200,\n            \"usd_earnings\": 125.50,\n            \"alpha_earnings\": 1025.75,\n            \"accounts_connected\": 42,\n            \"total_subscribers\": 125000\n        }\n    ],\n    \"total_count\": 1\n}\n```\n\n**Performance:**\n- Uses optimized queries with date index\n- Includes 1-hour response caching for optimal performance\n- Handles large date ranges efficiently\n\n**Error Responses:**\n- **200 with empty array**: No data exists for the specified date range (valid response)\n- **500**: Internal server error or database connectivity issues","operationId":"get_youtube_daily_summary_api_v2_youtube_daily_summary_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Start date for filtering summaries (YYYY-MM-DD, inclusive)","title":"Start Date"},"description":"Start date for filtering summaries (YYYY-MM-DD, inclusive)"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"End date for filtering summaries (YYYY-MM-DD, inclusive)","title":"End Date"},"description":"End date for filtering summaries (YYYY-MM-DD, inclusive)"},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Maximum number of days to return (most recent first)","title":"Limit"},"description":"Maximum number of days to return (most recent first)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/YouTubeDailySummaryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/daily-summary/health":{"get":{"tags":["youtube-v2","youtube-daily-summary","health"],"summary":"Youtube Daily Summary Service Health","description":"Health check endpoint for the YouTube daily summary service.\n\nTests database connectivity specifically for daily summary operations.\n\n**Returns:**\n```json\n{\n    \"status\": \"healthy\",\n    \"service\": \"youtube-daily-summary\",\n    \"database_accessible\": true,\n    \"timestamp\": 1642682400.0\n}\n```\n\n**Status Codes:**\n- **200**: Service is healthy\n- **503**: Service is unhealthy (database issues)","operationId":"youtube_daily_summary_service_health_api_v2_youtube_daily_summary_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceHealthResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/geographic":{"get":{"tags":["youtube-v2","youtube-geographic"],"summary":"Get Youtube Geographic","description":"Retrieve geographic heatmap data showing watch time by country for YouTube videos matched to briefs.\n\nThis endpoint provides aggregated watch time metrics by country across all YouTube videos\nthat have been matched to briefs. It uses only the latest validator run per video to avoid\ndouble-counting.\n\n**Caching:**\n- Response is cached for 6 hours (21,600 seconds)\n- Cache key includes limit parameter\n\n**Query Parameters:**\n- **limit**: Maximum number of countries to return (0 = all, default 0)\n\n**Returns:**\nList of countries ordered by total watch time (descending)\n\n**Response Format:**\n```json\n{\n    \"data\": [\n        {\n            \"country_code\": \"US\",\n            \"watch_time_hours\": 1234.56,\n            \"watch_time_minutes\": 74073,\n            \"unique_videos\": 42\n        },\n        {\n            \"country_code\": \"GB\",\n            \"watch_time_hours\": 567.89,\n            \"watch_time_minutes\": 34073,\n            \"unique_videos\": 38\n        }\n    ],\n    \"meta\": {\n        \"timestamp\": \"2026-01-23T12:00:00\",\n        \"total_countries\": 50,\n        \"cached\": true\n    }\n}\n```\n\n**Key Logic:**\n- Only uses latest validator run per video to avoid double-counting\n- Only includes videos that have been matched to briefs\n- Watch time is converted from minutes to hours for display\n\n**Data Source:**\n- Uses `yt_video_run_breakdown` table with type='country_minutes'\n- Filtered through `video_matched_to_brief` to only include matched videos\n\n**Performance:**\n- Uses optimized queries with proper indexing\n- Includes 6-hour response caching for optimal performance\n\n**Error Responses:**\n- **200 with empty array**: No geographic data exists (valid response)\n- **500**: Internal server error or database connectivity issues","operationId":"get_youtube_geographic_api_v2_youtube_geographic_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Maximum number of countries to return (0 = all)","default":0,"title":"Limit"},"description":"Maximum number of countries to return (0 = all)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeographicResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/geographic/health":{"get":{"tags":["youtube-v2","youtube-geographic","health"],"summary":"Youtube Geographic Service Health","description":"Health check endpoint for the YouTube geographic service.\n\nTests database connectivity specifically for geographic operations.\n\n**Returns:**\n```json\n{\n    \"status\": \"healthy\",\n    \"service\": \"youtube-geographic\",\n    \"database_accessible\": true,\n    \"timestamp\": 1642682400.0\n}\n```\n\n**Status Codes:**\n- **200**: Service is healthy\n- **503**: Service is unhealthy (database issues)","operationId":"youtube_geographic_service_health_api_v2_youtube_geographic_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceHealthResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/youtube/agency/info":{"get":{"tags":["youtube-v2","agency"],"summary":"Get Agency Info","description":"Return the agency info for an agency admin.\n\nDefaults to the authenticated user; an admin/API-key caller may pass\nportal_user_id to view a specific agency admin's agency (used by the\nportal during admin impersonation).","operationId":"get_agency_info_api_v2_youtube_agency_info_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Portal User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgencyInfoResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/agency/creators":{"get":{"tags":["youtube-v2","agency"],"summary":"Get Agency Creators","description":"Return creators belonging to the agency.\nIncludes earnings data using the same weight-correction CTE pattern as get_users_list().\n\nDefaults to the authenticated user; an admin/API-key caller may pass\nportal_user_id to view a specific agency admin's creators (used by the\nportal during admin impersonation).","operationId":"get_agency_creators_api_v2_youtube_agency_creators_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"portal_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Portal User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgencyCreatorsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/agency/{agency_id}/videos":{"get":{"tags":["youtube-v2","agency"],"summary":"Get Agency Videos","description":"Return paginated list of videos across all agency creators with\ntwo-tier margin breakdown (creator vs agency earnings).","operationId":"get_agency_videos_api_v2_youtube_agency__agency_id__videos_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"agency_id","in":"path","required":true,"schema":{"type":"integer","description":"Agency ID","title":"Agency Id"},"description":"Agency ID"},{"name":"date_lookback_days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"Days to look back for videos","default":30,"title":"Date Lookback Days"},"description":"Days to look back for videos"},{"name":"creator_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filter by creator portal_user_id","title":"Creator Id"},"description":"Filter by creator portal_user_id"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Results per page","default":50,"title":"Limit"},"description":"Results per page"},{"name":"portal_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Target user id (admin impersonation)","title":"Portal User Id"},"description":"Target user id (admin impersonation)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgencyVideoListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/agency/{agency_id}/earnings":{"get":{"tags":["youtube-v2","agency"],"summary":"Get Agency Earnings","description":"Return daily + monthly aggregated agency earnings across all creators.","operationId":"get_agency_earnings_api_v2_youtube_agency__agency_id__earnings_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"agency_id","in":"path","required":true,"schema":{"type":"integer","description":"Agency ID","title":"Agency Id"},"description":"Agency ID"},{"name":"months_lookback","in":"query","required":false,"schema":{"type":"integer","maximum":36,"minimum":1,"description":"Months to look back","default":12,"title":"Months Lookback"},"description":"Months to look back"},{"name":"portal_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Target user id (admin impersonation)","title":"Portal User Id"},"description":"Target user id (admin impersonation)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgencyEarningsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/youtube/agency/{agency_id}/videos/{video_id}":{"get":{"tags":["youtube-v2","agency"],"summary":"Get Agency Video Detail","description":"Return full detail for a single video belonging to an agency creator,\nwith two-tier margin breakdown and daily time-series.","operationId":"get_agency_video_detail_api_v2_youtube_agency__agency_id__videos__video_id__get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"agency_id","in":"path","required":true,"schema":{"type":"integer","description":"Agency ID","title":"Agency Id"},"description":"Agency ID"},{"name":"video_id","in":"path","required":true,"schema":{"type":"integer","description":"Internal video ID (id_video)","title":"Video Id"},"description":"Internal video ID (id_video)"},{"name":"portal_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Target user id (admin impersonation)","title":"Portal User Id"},"description":"Target user id (admin impersonation)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgencyVideoDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/accounts":{"get":{"tags":["twitter-v2","twitter-accounts"],"summary":"Get Accounts","description":"Get Twitter accounts with optional filtering by pool.\n\nThis endpoint consolidates two previous patterns:\n1. All accounts across all pools (no query params)\n2. Accounts for specific pool with optional adjacency matrix (with ?pool=)\n\n**Query Parameters:**\n- **pool**: Filter to specific pool (e.g., \"elite\", \"challenger\", \"rising\")\n- **include_matrix**: Include adjacency matrix data (requires pool parameter, returns 400 if missing)\n- **date**: (Optional) Date in YYYY-MM-DD format - finds most recent validator run <= this date\n- **limit**: (Optional) Limit results to top N accounts, sorted by score descending (minimum: 1)\n\n**Examples:**\n```\nGET /twitter/accounts\n→ Returns all accounts across all pools\n\nGET /twitter/accounts?pool=elite\n→ Returns elite pool accounts with scores\n\nGET /twitter/accounts?pool=elite&limit=50\n→ Returns top 50 accounts from elite pool (by score)\n\nGET /twitter/accounts?pool=elite&include_matrix=true\n→ Returns elite pool accounts with adjacency matrix\n\nGET /twitter/accounts?include_matrix=true\n→ Returns 400 error: \"include_matrix requires pool parameter\"\n```\n\n**Response Fields:**\n- **total_count**: Number of accounts returned (after applying limit if specified)\n- **total_before_limit**: Total accounts available before limit (null if no limit applied)\n\n**Caching:**\n- Response is cached for 10 minutes (600 seconds)\n- Cache key includes pool, date, include_matrix, and limit parameters\n\n**Status Codes:**\n- **200**: Success (may return empty if no accounts found)\n- **400**: Invalid parameters (include_matrix without pool, invalid date format, limit < 1)\n- **500**: Database error","operationId":"get_accounts_api_v2_twitter_accounts_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"pool","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by pool name (e.g., 'elite', 'challenger')","title":"Pool"},"description":"Filter by pool name (e.g., 'elite', 'challenger')"},{"name":"include_matrix","in":"query","required":false,"schema":{"type":"boolean","description":"Include adjacency matrix (requires pool parameter)","default":false,"title":"Include Matrix"},"description":"Include adjacency matrix (requires pool parameter)"},{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional date in YYYY-MM-DD format (finds most recent run <= this date)","title":"Date"},"description":"Optional date in YYYY-MM-DD format (finds most recent run <= this date)"},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Limit results to top N accounts (sorted by score descending)","title":"Limit"},"description":"Limit results to top N accounts (sorted by score descending)"},{"name":"registered","in":"query","required":false,"schema":{"type":"boolean","description":"Filter to only registered (connected) accounts","default":false,"title":"Registered"},"description":"Filter to only registered (connected) accounts"},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by account category (e.g., 'kol', 'pm_exchange', 'bittensor_subnet')","title":"Category"},"description":"Filter by account category (e.g., 'kol', 'pm_exchange', 'bittensor_subnet')"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/TwitterAccountScoresResponse"},{"$ref":"#/components/schemas/TwitterAllAccountsResponse"}],"title":"Response Get Accounts Api V2 Twitter Accounts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/accounts/earnings":{"get":{"tags":["twitter-v2","twitter-accounts"],"summary":"Get All Accounts Earnings","description":"Retrieve earnings for ALL X/Twitter accounts that have received payments.\n\nReturns all X accounts that have earned money (completed/confirmed payments), including:\n- **Connected accounts**: Accounts with records in x_connections (linked to coldkeys)\n- **Unconnected accounts**: Accounts with earnings but no x_connections record\n\n**Caching:** 1 hour (3,600 seconds)","operationId":"get_all_accounts_earnings_api_v2_twitter_accounts_earnings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/XAllAccountsEarningsResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/twitter/accounts/distribution":{"get":{"tags":["twitter-v2","twitter-accounts"],"summary":"Get X Distribution","description":"Get X/Twitter earnings distribution with paid vs pending breakdown per account.\n\n**Earnings Breakdown:**\n- **Paid (completed)**: Sum of usd_amount from payments table where status = 'completed'\n- **Pending (weight>0)**: Sum of latest usd_target per tweet from latest validatorRun where weight > 0\n- **Total**: paid + pending\n\n**Query Parameters:**\n- **timeframe**: `'30d'` (last 30 days) or `'all'` (all-time, default)\n\n**Caching:** 1 hour per timeframe","operationId":"get_x_distribution_api_v2_twitter_accounts_distribution_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"timeframe","in":"query","required":false,"schema":{"type":"string","description":"Timeframe filter: '30d' for last 30 days, 'all' for all-time","default":"all","title":"Timeframe"},"description":"Timeframe filter: '30d' for last 30 days, 'all' for all-time"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/XDistributionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/accounts/{username}":{"get":{"tags":["twitter-v2","twitter-accounts","twitter"],"summary":"Get details for a single Twitter account by username","description":"Get detailed information for a single Twitter account by username.\n\nReturns account details including:\n- Username and Bitcast account ID\n- Follower count\n- Connected status (whether account has records in x_connections)\n- Scores for each pool the account is a member of (from most recent validator runs)\n- Highest score across all pools\n- Top 20 connections ranked by bidirectional relationship strength\n- Payment wallet address (connected coldkey)\n- Referral code (Base64url-encoded username for sharing)\n\n**Top Connections:**\n- Calculated using smoothed geometric mean of bidirectional relationship weights from adjacency matrix\n- connection_strength = sqrt((weight_A_to_B + ε) * (weight_B_to_A + ε)) where ε = 0.001\n- One-way connections receive low but non-zero scores, mutual connections receive significantly higher scores\n- Sorted by connection_strength descending, then influence_score descending\n- Only includes connections with at least one non-zero weight\n- May return fewer than 20 if not enough connections exist\n\n**Example Request:**\n```\nGET /api/v1/twitter/accounts/cryptouser1\n```\n\n**Example Response:**\n```json\n{\n  \"username\": \"cryptouser1\",\n  \"bitcast_account_id\": \"bc_account_123\",\n  \"followers\": 1250,\n  \"connected\": true,\n  \"pool_scores\": [\n    {\n      \"pool\": \"elite\",\n      \"score\": 0.95432,\n      \"rank\": 3,\n      \"pool_size\": 150,\n      \"validator_run_date\": \"2025-12-08T10:30:00\"\n    },\n    {\n      \"pool\": \"challenger\",\n      \"score\": 0.87654,\n      \"rank\": 12,\n      \"pool_size\": 200,\n      \"validator_run_date\": \"2025-12-08T10:30:00\"\n    }\n  ],\n  \"highest_score\": 0.95432,\n  \"top_connections\": [\n    {\n      \"username\": \"connected_user1\",\n      \"pool\": \"elite\",\n      \"connection_strength\": 0.85,\n      \"influence_score\": 0.92\n    },\n    {\n      \"username\": \"connected_user2\",\n      \"pool\": \"elite\",\n      \"connection_strength\": 0.72,\n      \"influence_score\": 0.88\n    }\n  ],\n  \"payment_wallet\": \"5GHqJxPX5aYqKMT7JNaZJcKmMbNRpP8JCYHqJxPX5aYqKMT7\",\n  \"referral_code\": \"Y3J5cHRvdXNlcjE\"\n}\n```\n\n**Status Codes:**\n- **200**: Account found and details returned successfully\n- **404**: Account not found\n- **500**: Database error occurred\n\n**Caching:**\n- Response is cached for 10 minutes (600 seconds)\n- Cache key includes username\n\n**Notes:**\n- Only includes scores from the most recent validator run for each pool\n- If account exists but has no scores, returns empty pool_scores array\n- Top connections are calculated efficiently, skipping pairs where either direction weight is 0\n- Referral code is auto-generated from the username using Base64url encoding","operationId":"get_account_by_username_api_v2_twitter_accounts__username__get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"username","in":"path","required":true,"schema":{"type":"string","description":"Twitter username/handle to retrieve","title":"Username"},"description":"Twitter username/handle to retrieve"}],"responses":{"200":{"description":"Successfully retrieved account details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterAccountDetailResponse"}}}},"404":{"description":"Account not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/accounts/{username}/private":{"get":{"tags":["twitter-v2","twitter-accounts","twitter"],"summary":"Get private account data for a logged-in user","description":"Get private account data for a Twitter account.\n\nThis endpoint returns sensitive data intended for logged-in users only:\n- Total tweets matched to briefs\n- Total earnings (from completed/confirmed payments)\n- Average earned per tweet\n- Payment wallet (connected coldkey)\n- Rank history by pool (past 12 months)\n- Earnings per day (past 12 months)\n- Last 30 tweets matched with briefs (including content, views, engagements, and detailed engagement metrics)\n\n**Example Request:**\n```\nGET /api/v1/twitter/accounts_private/cryptouser1\n```\n\n**Example Response:**\n```json\n{\n  \"username\": \"cryptouser1\",\n  \"total_tweets\": 45,\n  \"total_earnings\": 1250.50,\n  \"avg_earned_per_tweet\": 27.79,\n  \"payment_wallet\": \"5GHqJxPX5aYqKMT7...\",\n  \"rank_history\": {\n    \"elite\": [\n      {\"date\": \"2025-12-08\", \"rank\": 3, \"pool_size\": 150}\n    ]\n  },\n  \"earnings_per_day\": {\n    \"2025-12-08\": 50.25\n  },\n  \"recent_tweets\": [\n    {\n      \"id\": \"1234567890\",\n      \"username\": \"cryptouser1\",\n      \"reward\": 25.50,\n      \"score\": 0.85,\n      \"views\": 5000,\n      \"engagements\": 208,\n      \"favourites\": 120,\n      \"retweets\": 45,\n      \"replies\": 23,\n      \"quotes\": 5,\n      \"bookmarks\": 15,\n      \"content\": \"Tweet text...\",\n      \"brief_id\": \"campaign_xyz\",\n      \"published_at\": \"2025-12-08T10:30:00\"\n    }\n  ]\n}\n```\n\n**Status Codes:**\n- **200**: Account found and data returned successfully\n- **404**: Account not found\n- **500**: Database error occurred\n\n**Caching:**\n- Response is cached for 10 minutes (600 seconds)\n- Cache key includes username","operationId":"get_private_account_by_username_api_v2_twitter_accounts__username__private_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"username","in":"path","required":true,"schema":{"type":"string","description":"Twitter username/handle to retrieve","title":"Username"},"description":"Twitter username/handle to retrieve"}],"responses":{"200":{"description":"Successfully retrieved private account data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterAccountPrivateResponse"}}}},"404":{"description":"Account not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/accounts/{username}/referral-targets":{"get":{"tags":["twitter-v2","twitter-accounts","twitter"],"summary":"Get unregistered KOLs sorted by connection strength for referral","description":"Get unregistered KOLs recommended for referral, sorted by connection strength.\n\nUses adjacency matrix data across all pools the user belongs to.\nOnly returns unregistered accounts (no connected wallet) that have a\nnon-zero connection strength with the requesting user.\n\n**Connection Strength:** Smoothed geometric mean of bidirectional weights:\nsqrt((weight_A_to_B + 0.001) * (weight_B_to_A + 0.001))\nMutual connections score higher than one-way connections.","operationId":"get_referral_targets_api_v2_twitter_accounts__username__referral_targets_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"username","in":"path","required":true,"schema":{"type":"string","description":"Twitter username/handle","title":"Username"},"description":"Twitter username/handle"}],"responses":{"200":{"description":"Successfully retrieved referral targets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralTargetsResponse"}}}},"404":{"description":"Account not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/briefs":{"get":{"tags":["twitter-v2","twitter-briefs","twitter"],"summary":"Get all Twitter briefs with key details","description":"Get list of all Twitter briefs with key metrics, optionally filtered by pool and/or username eligibility.\n\nReturns brief details including:\n- Brief ID and description\n- Post count (number of tweets matched)\n- Total views across all tweets\n- Budget information (from active version)\n- Maximum tweets, members, and considered limits\n- Start and end dates (from active version)\n- Status (Upcoming/Live/Complete)\n- Pools (array), tag, and QRT information\n\n**Parameters:**\n- **pool** (optional): Pool name to filter briefs by (e.g., \"elite\", \"challenger\")\n  - Filters briefs that include this pool in their pools array\n- **username** (optional): Twitter username to filter briefs by rank eligibility\n  - When provided, only returns briefs where the account's rank in at least one \n    matching pool is <= the brief's max_members field\n  - Filters based on account rank from the most recent validator run for each pool\n\n**Example Request:**\n```\nGET /api/v2/twitter/briefs?pool=elite&username=example_user\n```\n\n**Example Response:**\n```json\n{\n  \"briefs\": [\n    {\n      \"brief_id\": \"crypto_12345\",\n      \"brief\": \"Promote Product X\",\n      \"Posts\": 45,\n      \"views\": 125000,\n      \"budget\": 5000,\n      \"max_tweets\": 1,\n      \"max_members\": 150,\n      \"max_considered\": 300,\n      \"start_date\": \"2025-12-15\",\n      \"end_date\": \"2025-12-20\",\n      \"status\": \"Live\",\n      \"pools\": [\"elite\", \"challenger\"],\n      \"tag\": \"crypto\",\n      \"qrt\": \"https://twitter.com/example/status/123456789\"\n    }\n  ],\n  \"total_count\": 1\n}\n```\n\n**Note:** Budget, start_date, end_date, and max_tweets come from the active version of the brief.","operationId":"get_twitter_briefs_api_v2_twitter_briefs_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"pool","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional pool name to filter briefs (e.g., 'elite', 'challenger')","title":"Pool"},"description":"Optional pool name to filter briefs (e.g., 'elite', 'challenger')"},{"name":"username","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional Twitter username to filter briefs based on rank eligibility","title":"Username"},"description":"Optional Twitter username to filter briefs based on rank eligibility"},{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Page number (1-based)","title":"Page"},"description":"Page number (1-based)"},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Items per page (default 12)","title":"Limit"},"description":"Items per page (default 12)"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status (Upcoming/Live/Complete)","title":"Status"},"description":"Filter by status (Upcoming/Live/Complete)"},{"name":"sort","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sort field: date|budget|impressions (default date)","title":"Sort"},"description":"Sort field: date|budget|impressions (default date)"},{"name":"sort_dir","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sort direction: asc|desc (default desc)","title":"Sort Dir"},"description":"Sort direction: asc|desc (default desc)"}],"responses":{"200":{"description":"Successfully retrieved Twitter briefs","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterBriefsListResponse"}}}},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["twitter-v2","twitter-briefs","twitter"],"summary":"Create a user-generated brief with funding","description":"Create a new user-generated Twitter brief with funding calculation.\n\nThis endpoint creates a brief and returns payment details including:\n- Destination address for payment\n- Crypto amount to send\n- Exchange rate used\n- Payment expiration time (15 minutes)\n\n**Returns:**\n- Brief details with funding_id (needed to submit payment)","operationId":"create_brief_with_funding_api_v2_twitter_briefs_post","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBriefFundingRequest"}}}},"responses":{"201":{"description":"Brief created successfully with funding record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefFundingResponse"}}}},"400":{"description":"Invalid request parameters or validation failed"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/briefs/{brief_id}":{"get":{"tags":["twitter-v2","twitter-briefs","twitter"],"summary":"Get detailed information for a specific Twitter brief","description":"Get detailed information for a specific Twitter brief.\n\nIncludes:\n- Brief description and display name\n- Budget details (from active version)\n- Start and end dates (from active version)\n- Status (Upcoming/Live/Complete)\n- Pools (array), tag, and QRT information\n- Performance metrics (posts, views, engagements, engagement_rate, favorites, retweets, replies, quotes, bookmarks)\n- List of tweets matched to this brief from most recent validator run (ordered by weight DESC)\n  Each tweet includes:\n  - id: Tweet ID (as string to preserve precision)\n  - username: X/Twitter username/handle of the tweet author\n  - reward: USD reward for this tweet\n  - score: Weight score\n  - views: Number of views\n  - engagements: Total engagements (sum of favourites, retweets, replies, quotes, bookmarks)\n  - favourites, retweets, replies, quotes, bookmarks: Individual engagement metrics\n  - retweeted_by: List of users who retweeted (JSON array)\n  - quoted_by: List of users who quoted (JSON array)\n  - content: Tweet text content\n\n**Note:** \n- Only tweets from the most recent validator run are included.\n- Budget, dates, and max_tweets come from the active version of the brief.","operationId":"get_twitter_brief_details_api_v2_twitter_briefs__brief_id__get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","description":"Brief ID to get details for","title":"Brief Id"},"description":"Brief ID to get details for"}],"responses":{"200":{"description":"Successfully retrieved brief details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterBriefDetailResponse"}}}},"404":{"description":"Brief not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/briefs/{brief_id}/analytics":{"get":{"tags":["twitter-v2","twitter-briefs","twitter"],"summary":"Get engagement analytics for a specific Twitter brief","description":"Get engagement analytics data for a specific Twitter brief.\n\nIncludes:\n- Cumulative time-series metrics (posts, views, engagements, favorites, retweets, quotes, replies, bookmarks)\n- Data timestamp\n\n**Note:** All timeseries metrics are cumulative (each date shows running total, not daily values)","operationId":"get_twitter_brief_analytics_api_v2_twitter_briefs__brief_id__analytics_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","description":"Brief ID to get analytics for","title":"Brief Id"},"description":"Brief ID to get analytics for"}],"responses":{"200":{"description":"Successfully retrieved analytics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterBriefAnalyticsResponse"}}}},"404":{"description":"Brief not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/briefs/{brief_id}/daily-views":{"get":{"tags":["twitter-v2","twitter-briefs","twitter"],"summary":"Get daily per-tweet views for a brief (stacked bar chart data)","description":"Get daily per-tweet views for a brief, suitable for stacked bar charts.\n\nFor each day that metrics were collected, returns the latest views per tweet.\nEach tweet is a separate segment in the stacked bar.\n\n**Response shape:** `{ days: [{ date, tweets: [{ tweet_id, username, content_snippet, views }] }] }`","operationId":"get_brief_daily_views_api_v2_twitter_briefs__brief_id__daily_views_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","description":"Brief ID to get daily views for","title":"Brief Id"},"description":"Brief ID to get daily views for"}],"responses":{"200":{"description":"Successfully retrieved daily views","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__schemas__twitter_brief__BriefDailyViewsResponse"}}}},"404":{"description":"Brief not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/briefs/{brief_id}/evaluations":{"get":{"tags":["twitter-v2","twitter-briefs","twitter"],"summary":"Get all evaluations for a brief (admin only)","description":"Get ALL tweet evaluations for a given brief — matched AND failed.\nRequires admin-level permission (level 2+).\n\nReturns:\n- Each evaluation with tweet content, username, matched status, reasoning, score, metrics\n- Summary counts for total, matched, and failed evaluations","operationId":"get_brief_evaluations_api_v2_twitter_briefs__brief_id__evaluations_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","description":"Brief ID to get evaluations for","title":"Brief Id"},"description":"Brief ID to get evaluations for"}],"responses":{"200":{"description":"Successfully retrieved evaluations","content":{"application/json":{"schema":{}}}},"404":{"description":"Brief not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/briefs/{brief_id}/topup":{"post":{"tags":["twitter-v2","twitter-briefs","twitter"],"summary":"Top up an existing brief with additional funding","description":"Top up an existing brief with additional funding.\n\nThis endpoint creates a new version of the brief with increased budget and returns\npayment details including:\n- Destination address for payment\n- Crypto amount to send\n- Exchange rate used\n- Payment expiration time (15 minutes)\n\n**Returns:**\n- Brief details with funding_id (needed to submit payment)","operationId":"topup_brief_funding_api_v2_twitter_briefs__brief_id__topup_post","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"string","title":"Brief Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TopUpBriefFundingRequest"}}}},"responses":{"200":{"description":"Top-up created successfully with funding record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefFundingResponse"}}}},"400":{"description":"Invalid request parameters or validation failed"},"404":{"description":"Brief not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/funding/{funding_id}/payment":{"post":{"tags":["twitter-v2","twitter-funding","twitter"],"summary":"Submit payment transaction hash and block number","description":"Submit payment transaction hash and block number to complete brief funding.\n\n**Validation checks (for TAO transactions):**\n- Transaction exists at the specified block number\n- Transaction was successful on-chain\n- Destination address matches expected address\n- Amount matches expected amount (within 0.1% tolerance)\n- Transaction is recent (< 1 hour old)\n- Transaction hash hasn't been used before\n\n**On success:**\n- Marks funding as 'complete'\n- Activates the brief\n- Returns confirmation message\n\n**On failure:**\n- Returns detailed error message explaining what went wrong\n- Funding remains in 'pending' state (can retry with valid transaction)","operationId":"submit_brief_payment_api_v2_twitter_funding__funding_id__payment_post","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"funding_id","in":"path","required":true,"schema":{"type":"integer","title":"Funding Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitPaymentRequest"}}}},"responses":{"200":{"description":"Payment validated and brief activated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitPaymentResponse"}}}},"400":{"description":"Payment validation failed (invalid transaction, wrong amount, expired, etc.)"},"404":{"description":"Funding record not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/funding/{funding_id}/cancel":{"post":{"tags":["twitter-v2","twitter-funding","twitter"],"summary":"Cancel a pending funding record","description":"Cancel a pending funding record.\n\nThis endpoint allows users to cancel a funding request when they navigate away\nfrom the payment page or decide not to proceed. Only funding records with \n'pending' status can be cancelled.\n\n**Actions:**\n- Marks funding status as 'cancelled'\n- Marks associated brief version as 'cancelled'\n- Allows user to create a new funding request without hitting the \"multiple pending\" validation\n\n**Note:** Cannot cancel funding that is already 'complete', 'expired', or 'failed'.","operationId":"cancel_brief_funding_api_v2_twitter_funding__funding_id__cancel_post","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"funding_id","in":"path","required":true,"schema":{"type":"integer","title":"Funding Id"}}],"responses":{"200":{"description":"Funding cancelled successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelFundingResponse"}}}},"400":{"description":"Cannot cancel funding (not in pending status or validation failed)"},"404":{"description":"Funding record not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/pools":{"get":{"tags":["twitter-v2","twitter-pools","twitter"],"summary":"[LEGACY] Get list of available Twitter pools","description":"**[LEGACY - Use /twitter/pool-configs instead]**\n\nGet list of available Twitter pool names from the x_pools table.\n\n**This endpoint is deprecated.** Please use `/twitter/pool-configs` for complete pool information \nincluding keywords, initial_accounts, constraints, and all other configuration details.\n\nBy default, returns only active pool names. Set `include_inactive=true` to include inactive pools.\n\n**Parameters:**\n- **include_inactive** (optional): If `true`, return all pools including inactive ones. Default is `false` (active only).\n\n**Example Request (active only):**\n```\nGET /api/v1/twitter/pools\n```\n\n**Example Request (all pools):**\n```\nGET /api/v1/twitter/pools?include_inactive=true\n```\n\n**Example Response:**\n```json\n{\n  \"pools\": [\"elite\", \"challenger\", \"rising\"],\n  \"total_count\": 3\n}\n```\n\n**Status Codes:**\n- **200**: Request processed successfully (returns empty list if no pools exist)\n- **500**: Database error occurred\n\n**Caching:**\n- Response is cached for 10 minutes (600 seconds)\n- Separate cache keys for active-only vs all pools\n\n**Migration Guide:**\n- Replace: `GET /api/v1/twitter/pools`\n- With: `GET /api/v1/twitter/pool-configs`\n- The new endpoint returns full pool configuration objects instead of just names","operationId":"get_available_pools_api_v2_twitter_pools_get","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"include_inactive","in":"query","required":false,"schema":{"type":"boolean","description":"Include inactive pools (default: False, returns only active pools)","default":false,"title":"Include Inactive"},"description":"Include inactive pools (default: False, returns only active pools)"}],"responses":{"200":{"description":"Successfully retrieved pool list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterPoolsResponse"}}}},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/pools/configs":{"get":{"tags":["twitter-v2","twitter-pools","twitter"],"summary":"Get Twitter pool configurations (all x_pools fields)","description":"Get detailed configurations for Twitter pools from the x_pools table.\n\n**This is the recommended endpoint for pool information.** Returns all x_pools table fields including:\n- Pool ID and name\n- Keywords to track\n- Initial seed accounts\n- Core tier constraints (core_max_seed_accounts, core_min_interaction_weight, core_min_tweets)\n- Extended tier constraints (extended_max_seed_accounts, extended_min_interaction_weight, extended_min_tweets)\n- Discovery settings (max_discovery_iterations, convergence_threshold)\n- Language filter\n- Date offset for refresh date calculation\n- Active status\n- Timestamps\n- Next refresh date (calculated from reference date 2025-11-09, every 2nd Sunday, plus date_offset)\n\nBy default, returns only active pools. Set `include_inactive=true` to include inactive pools.\n\n**Parameters:**\n- **include_inactive** (optional): If `true`, return all pools including inactive ones. Default is `false` (active only).\n\n**Example Request (active only):**\n```\nGET /api/v1/twitter/pool-configs\n```\n\n**Example Request (all pools):**\n```\nGET /api/v1/twitter/pool-configs?include_inactive=true\n```\n\n**Example Response:**\n```json\n{\n  \"pools\": [\n    {\n      \"id_x_pools\": 1,\n      \"name\": \"elite\",\n      \"keywords\": [\"crypto\", \"blockchain\"],\n      \"initial_accounts\": [\"user1\", \"user2\"],\n      \"lang\": \"en\",\n      \"active\": true,\n      \"date_offset\": 0,\n      \"core_min_interaction_weight\": 5,\n      \"core_min_tweets\": 10,\n      \"core_max_seed_accounts\": 100,\n      \"extended_min_interaction_weight\": 1,\n      \"extended_min_tweets\": 1,\n      \"extended_max_seed_accounts\": 300,\n      \"max_discovery_iterations\": 3,\n      \"convergence_threshold\": 0.95,\n      \"created_at\": \"2025-01-01T00:00:00\",\n      \"updated_at\": \"2025-01-01T00:00:00\",\n      \"next_refresh_date\": \"2026-02-09\"\n    }\n  ],\n  \"total_count\": 1\n}\n```\n\n**Status Codes:**\n- **200**: Request processed successfully (returns empty list if no pools exist)\n- **500**: Database error occurred\n\n**Caching:**\n- Response is cached for 10 minutes (600 seconds)\n- Separate cache keys for active-only vs all pools","operationId":"get_pool_configurations_api_v2_twitter_pools_configs_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"include_inactive","in":"query","required":false,"schema":{"type":"boolean","description":"Include inactive pools (default: False, returns only active pools)","default":false,"title":"Include Inactive"},"description":"Include inactive pools (default: False, returns only active pools)"}],"responses":{"200":{"description":"Successfully retrieved pool configurations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoolConfigsResponse"}}}},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/pools/{pool_name}/metrics":{"get":{"tags":["twitter-v2","twitter-pools","twitter"],"summary":"Get pool metrics including follower statistics and network analysis","description":"Get comprehensive metrics for a Twitter pool using the latest available data.\n\nReturns account count, follower statistics, network analysis metrics, and earnings metrics:\n\n**Account Count:**\n- **account_count**: Total number of accounts in the pool\n- **connected_account_count**: Number of accounts in the pool that have a connected X account (record in x_connections)\n\n**Follower Metrics:**\n- **total_followers**: Sum of all followers across accounts in the pool\n- **median_followers**: Median follower count in the pool\n\n**Network Metrics:**\n- **k_core_10**: Number of nodes with 10 or more incoming connections\n- **top_150_reciprocity**: Percentage of edges that are bidirectional among the top 150 nodes (0-1 range)\n- **top_150_edge_density**: Edge density among top 150 nodes (actual edges / total possible edges, 0-1 range)\n\n**Earnings Metrics:**\n- **total_tweets**: Number of tweets from pool accounts that matched to briefs targeting this pool\n- **total_earnings**: Total USD earned from tweets matched to briefs targeting this pool (sum of total_usd_target from x_tweet_matched_to_brief)\n- **avg_earnings_per_tweet**: Average earnings per tweet in USD (total_earnings / total_tweets)\n\n**Example Request:**\n```\nGET /api/v2/twitter/pools/elite/metrics\n```\n\n**Example Response:**\n```json\n{\n  \"pool\": \"elite\",\n  \"account_count\": 150,\n  \"connected_account_count\": 142,\n  \"follower_metrics\": {\n    \"total_followers\": 1250000,\n    \"median_followers\": 5420\n  },\n  \"network_metrics\": {\n    \"k_core_10\": 45,\n    \"top_150_reciprocity\": 0.67,\n    \"top_150_edge_density\": 0.42\n  },\n  \"earnings_metrics\": {\n    \"total_tweets\": 4500,\n    \"total_earnings\": 125000.50,\n    \"avg_earnings_per_tweet\": 27.79\n  }\n}\n```\n\n**Status Codes:**\n- **200**: Request processed successfully\n- **404**: Pool not found or no data available\n- **500**: Database error occurred\n\n**Caching:**\n- Response is cached for 10 minutes (600 seconds)\n- Cache key includes pool name\n\n**Notes:**\n- Uses the most recent validator run with scores for the pool\n- Network metrics require adjacency matrix data\n- If adjacency matrix is not available, network metrics will be 0\n- Earnings metrics only count activity from briefs targeting this specific pool\n- Tweets are only counted if they match to briefs that include this pool in their pools array\n- Earnings come from total_usd_target in x_tweet_matched_to_brief (not the Payment table)\n- Connected accounts are those with records in the x_connections table (indicating they have linked their X account)","operationId":"get_pool_metrics_api_v2_twitter_pools__pool_name__metrics_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"pool_name","in":"path","required":true,"schema":{"type":"string","title":"Pool Name"}}],"responses":{"200":{"description":"Successfully retrieved pool metrics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoolMetricsResponse"}}}},"404":{"description":"Pool not found"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/pools/{pool_name}/score-changes":{"get":{"tags":["twitter-v2","twitter-pools","twitter"],"summary":"Get score changes between last two validator runs for a pool","description":"Compare per-account scores between the two most recent validator runs for a pool.\n\nReturns each account's current score, previous score, score delta, rank, rank change,\nand whether they are a new entrant (not in previous run).\n\n**Color mapping for treemap:**\n- Positive `score_change` → green (rising influence)\n- Negative `score_change` → red (falling influence)\n- `is_new = true` → gray (no prior data)\n\n**Example Request:**\n```\nGET /api/v2/twitter/pools/elite/score-changes\n```\n\n**Example Response:**\n```json\n{\n  \"pool\": \"elite\",\n  \"current_run_date\": \"2026-05-30T10:00:00\",\n  \"previous_run_date\": \"2026-05-16T10:00:00\",\n  \"accounts\": [\n    {\n      \"username\": \"cryptouser1\",\n      \"display_name\": \"Crypto User\",\n      \"followers\": 12500,\n      \"connected\": true,\n      \"current_score\": 0.95432,\n      \"previous_score\": 0.92100,\n      \"score_change\": 0.03332,\n      \"rank\": 1,\n      \"previous_rank\": 3,\n      \"rank_change\": 2,\n      \"is_new\": false\n    }\n  ],\n  \"total_count\": 150\n}\n```\n\n**Caching:** 10 minutes (600 seconds)","operationId":"get_score_changes_api_v2_twitter_pools__pool_name__score_changes_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"pool_name","in":"path","required":true,"schema":{"type":"string","title":"Pool Name"}}],"responses":{"200":{"description":"Successfully retrieved score changes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScoreChangesResponse"}}}},"404":{"description":"Pool not found or no data available"},"500":{"description":"Internal server error"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/coldkeys/{coldkey}":{"post":{"tags":["twitter-v2","twitter-coldkeys","twitter"],"summary":"Register a coldkey and get registration tag (legacy)","description":"**[DEPRECATED]** Register a coldkey and receive a unique registration tag.\n\n**This endpoint is deprecated. Please use `POST /twitter/register-coldkey` (without path parameter) instead,\nwhich supports additional fields like token and referred_by.**\n\nThis endpoint is **idempotent** - if the coldkey is already registered,\nit will return the existing tag. If not, it creates a new entry and returns\na new tag.\n\nThe registration tag is generated deterministically using SHA-256 hash:\n`tag = f\"Stitch3-{sha256(coldkey)[:8]}\"`\n(Legacy endpoint does not support referral suffix)\n\n**Coldkey Format:**\n- Bittensor substrate wallet address\n- 40-50 characters in length\n- Base58 encoded (alphanumeric, excluding 0, O, I, l)\n\n**Example Request:**\n```\nPOST /api/v1/twitter/register-coldkey/5GHqJxPX5aYqKMT7JNaZJcKmMbNRpP8JCYHqJxPX5aYqKMT7\n```\n\n**Example Response:**\n```json\n{\n  \"tag\": \"Stitch3-a3c5e8f1\"\n}\n```\n\n**Idempotency:**\nCalling this endpoint multiple times with the same coldkey will always\nreturn the same tag, ensuring safe retry behavior.\n\n**Args:**\n- **coldkey**: The substrate wallet address to register (in URL path)\n\n**Returns:**\n- **tag**: The registration tag for the coldkey\n\n**Raises:**\n- **422**: Invalid coldkey format (validation error)\n- **500**: Database error or unexpected server error","operationId":"register_coldkey_legacy_api_v2_twitter_coldkeys__coldkey__post","deprecated":true,"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"coldkey","in":"path","required":true,"schema":{"type":"string","minLength":40,"maxLength":50,"pattern":"^[1-9A-HJ-NP-Za-km-z]{40,50}$","description":"Bittensor substrate wallet address (coldkey)","title":"Coldkey"},"description":"Bittensor substrate wallet address (coldkey)"}],"responses":{"200":{"description":"Successfully registered coldkey (or returned existing tag)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ColdkeyRegistrationResponse"},"example":{"tag":"Stitch3-a3c5e8f1"}}}},"422":{"description":"Invalid coldkey format","content":{"application/json":{"example":{"detail":[{"loc":["path","coldkey"],"msg":"Coldkey must contain only valid base58 characters","type":"value_error"}]}}}},"500":{"description":"Internal server error"}}}},"/api/v2/twitter/coldkeys":{"post":{"tags":["twitter-v2","twitter-coldkeys","twitter"],"summary":"Register a coldkey with additional metadata and get registration tag","description":"Register a coldkey and receive a unique registration tag.\n\nThis endpoint is **idempotent** on the coldkey + referral combination.\nThe same coldkey can generate multiple tags with different referrals.\nIf the exact coldkey + referral combination already exists, returns the existing tag.\n\nThe registration tag is generated deterministically using SHA-256 hash:\n`tag = f\"Stitch3-{sha256(coldkey)[:8]}\"`\nIf referred_by is provided: `tag = f\"Stitch3-{sha256(coldkey)[:8]}-{referred_by}\"`\n\n**Coldkey Format:**\n- Bittensor substrate wallet address\n- 40-50 characters in length\n- Base58 encoded (alphanumeric, excluding 0, O, I, l)\n\n**Example Request:**\n```json\n{\n  \"coldkey\": \"5GHqJxPX5aYqKMT7JNaZJcKmMbNRpP8JCYHqJxPX5aYqKMT7\",\n  \"token\": \"bitcast\",\n  \"referred_by\": \"user123\",\n  \"x_handle\": \"alice_crypto\"\n}\n```\n\n**Example Response (with referral):**\n```json\n{\n  \"tag\": \"Stitch3-a3c5e8f1-user123\"\n}\n```\n\n**Example Response (without referral):**\n```json\n{\n  \"tag\": \"Stitch3-a3c5e8f1\"\n}\n```\n\n**Idempotency:**\nCalling this endpoint multiple times with the same coldkey will always\nreturn the same tag, ensuring safe retry behavior.\n\n**Args:**\n- **coldkey**: The substrate wallet address to register (required)\n- **token**: Token for no-code coldkey (required)\n- **referred_by**: Referral information (optional)\n- **x_handle**: X/Twitter handle (optional)\n\n**Returns:**\n- **tag**: The registration tag for the coldkey\n\n**Raises:**\n- **422**: Invalid coldkey format or invalid referral code (validation error)\n- **500**: Database error or unexpected server error","operationId":"register_coldkey_api_v2_twitter_coldkeys_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ColdkeyRegistrationRequest"}}},"required":true},"responses":{"200":{"description":"Successfully registered coldkey (or returned existing tag)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ColdkeyRegistrationResponse"},"example":{"tag":"Stitch3-a3c5e8f1"}}}},"422":{"description":"Invalid coldkey format","content":{"application/json":{"example":{"detail":[{"loc":["body","coldkey"],"msg":"Coldkey must contain only valid base58 characters","type":"value_error"}]}}}},"500":{"description":"Internal server error"}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/twitter/daily-summary":{"get":{"tags":["twitter-v2","twitter-daily-summary"],"summary":"Get Twitter Daily Summary","description":"Retrieve Twitter/X platform-wide daily summary metrics.\n\nThis endpoint provides aggregated daily metrics across the entire Twitter/X platform including:\n- Tweets published per day\n- Total views per day\n- Engagement metrics (retweets, quotes, likes, bookmarks, replies)\n- Earnings (USD and Alpha)\n- User registrations\n\n**Caching:**\n- Response is cached for 1 hour (3,600 seconds)\n- Cache key includes start_date, end_date, and limit parameters\n\n**Query Parameters:**\n- **start_date**: Optional start date filter (YYYY-MM-DD, inclusive)\n- **end_date**: Optional end date filter (YYYY-MM-DD, inclusive)\n- **limit**: Optional maximum number of days to return (most recent first)\n\n**Returns:**\nList of daily summaries ordered by date (most recent first)\n\n**Response Format:**\n```json\n{\n    \"daily_summaries\": [\n        {\n            \"date\": \"2026-01-26\",\n            \"tweets\": 150,\n            \"views\": 250000,\n            \"retweets\": 1200,\n            \"quotes\": 350,\n            \"likes\": 4500,\n            \"bookmarks\": 800,\n            \"replies\": 600,\n            \"registrations\": 25,\n            \"usd_earnings\": 250.75,\n            \"alpha_earnings\": 2050.50\n        }\n    ],\n    \"total_count\": 1\n}\n```\n\n**Performance:**\n- Uses optimized queries with date index\n- Includes 1-hour response caching for optimal performance\n- Handles large date ranges efficiently\n\n**Error Responses:**\n- **200 with empty array**: No data exists for the specified date range (valid response)\n- **500**: Internal server error or database connectivity issues","operationId":"get_twitter_daily_summary_api_v2_twitter_daily_summary_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Start date for filtering summaries (YYYY-MM-DD, inclusive)","title":"Start Date"},"description":"Start date for filtering summaries (YYYY-MM-DD, inclusive)"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"End date for filtering summaries (YYYY-MM-DD, inclusive)","title":"End Date"},"description":"End date for filtering summaries (YYYY-MM-DD, inclusive)"},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Maximum number of days to return (most recent first)","title":"Limit"},"description":"Maximum number of days to return (most recent first)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterDailySummaryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/daily-summary/health":{"get":{"tags":["twitter-v2","twitter-daily-summary","health"],"summary":"Twitter Daily Summary Service Health","description":"Health check endpoint for the Twitter daily summary service.\n\nTests database connectivity specifically for daily summary operations.\n\n**Returns:**\n```json\n{\n    \"status\": \"healthy\",\n    \"service\": \"twitter-daily-summary\",\n    \"database_accessible\": true,\n    \"timestamp\": 1642682400.0\n}\n```\n\n**Status Codes:**\n- **200**: Service is healthy\n- **503**: Service is unhealthy (database issues)","operationId":"twitter_daily_summary_service_health_api_v2_twitter_daily_summary_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceHealthResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/twitter/stats":{"get":{"tags":["twitter-v2","twitter-stats"],"summary":"Get Twitter Platform Stats","description":"Retrieve platform-wide all-time Twitter statistics.\n\nReturns two data points aggregated across the entire platform:\n\n- **avg_earned_per_tweet**: All-time average USD earned per tweet. Only tweets\n  that have received a reward (`total_usd_target > 0`) are included in the\n  denominator, consistent with per-account calculations.\n\n- **top_earning_tweet_usd**: The highest USD amount ever earned by a single tweet.\n\n**Caching:**\n- Response is cached for 1 hour (3,600 seconds)\n\n**Response Format:**\n```json\n{\n    \"avg_earned_per_tweet\": 27.79,\n    \"top_earning_tweet_usd\": 850.00\n}\n```\n\n**Status Codes:**\n- **200**: Success\n- **500**: Database error","operationId":"get_twitter_platform_stats_api_v2_twitter_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterPlatformStatsResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/twitter/referrals/me":{"get":{"tags":["twitter-v2","twitter-referrals"],"summary":"Get My Referrals","description":"Get the authenticated user's referral data.\n\nReturns all referrals where the user is either the referee or referrer,\nincluding pending signups and completed/paid referrals.\n\n**Query Parameters:**\n- **username**: X/Twitter username (required)\n\n**Response Fields:**\n- **referral_code**: The user's base64-encoded referral code\n- **referrals**: List of referrals with role, status, amounts, and tx hashes\n- **total_pending/completed/paid**: Counts by status\n- **total_earned_usd**: Total USD earned from paid referrals","operationId":"get_my_referrals_api_v2_twitter_referrals_me_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"username","in":"query","required":true,"schema":{"type":"string","description":"X/Twitter username to look up referrals for","title":"Username"},"description":"X/Twitter username to look up referrals for"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserReferralsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/twitter/referrals":{"get":{"tags":["twitter-v2","twitter-referrals"],"summary":"Get All Referrals","description":"Get platform-wide referral data.\n\nReturns all referrals across the platform with payment status,\nincluding pending signups and completed/paid referrals.\n\n**Response Fields:**\n- **total_referrals**: Total count of all referrals\n- **completed/pending**: Counts by status\n- **total_paid_usd**: Total USD paid out across all referrals\n- **referrals**: Full list with per-role amounts and tx hashes","operationId":"get_all_referrals_api_v2_twitter_referrals_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformReferralsResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/stats/creator-growth":{"get":{"tags":["stats-v2","stats-creator-growth"],"summary":"Get Creator Growth","description":"Retrieve daily cumulative creator counts by platform (YouTube + X/Twitter).\n\nReturns a time series of cumulative creator onboarding for use in stacked\narea charts on the dashboard.\n\n**Caching:**\n- Response is cached for 6 hours (21,600 seconds)\n\n**Response Format:**\n```json\n{\n    \"data\": [\n        {\n            \"date\": \"2025-08-22\",\n            \"youtube_creators\": 27,\n            \"x_creators\": 5,\n            \"total\": 32\n        }\n    ],\n    \"meta\": {\n        \"total_youtube\": 48,\n        \"total_x\": 115,\n        \"total_combined\": 163,\n        \"cached\": false,\n        \"timestamp\": \"2026-02-26T07:00:00\"\n    }\n}\n```\n\n**Data Sources:**\n- YouTube creators: `yt_account` table (`created_at`)\n- X creators: `x_accounts` table (`added`, filtered to registered accounts)\n\n**Status Codes:**\n- **200**: Success\n- **500**: Database error","operationId":"get_creator_growth_api_v2_stats_creator_growth_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatorGrowthResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/stats/creator-growth/health":{"get":{"tags":["stats-v2","stats-creator-growth","health"],"summary":"Creator Growth Service Health","description":"Health check endpoint for the creator growth service.\n\nTests database connectivity for stats operations.\n\n**Status Codes:**\n- **200**: Service is healthy\n- **503**: Service is unhealthy (database issues)","operationId":"creator_growth_service_health_api_v2_stats_creator_growth_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceHealthResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/stats/ecosystem-creator-growth":{"get":{"tags":["stats-v2","stats-ecosystem-creator-growth"],"summary":"Get Ecosystem Creator Growth","description":"Retrieve daily cumulative connected creator counts by ecosystem.\n\nEach creator is assigned to the ecosystem where they have the highest\ninfluence score. Returns a time series suitable for stacked line charts.\n\n**Caching:**\n- Response is cached for 6 hours (21,600 seconds)\n\n**Response Format:**\n```json\n{\n    \"data\": [\n        {\"date\": \"2025-11-11\", \"ecosystem\": \"tao\", \"creators\": 15},\n        {\"date\": \"2025-11-11\", \"ecosystem\": \"ai_agents\", \"creators\": 10},\n        {\"date\": \"2025-11-12\", \"ecosystem\": \"tao\", \"creators\": 49},\n        ...\n    ],\n    \"meta\": {\n        \"ecosystems\": [\"ai_agents\", \"prediction_markets\", \"tao\"],\n        \"total_creators\": 183,\n        \"date_range\": {\"start\": \"2025-11-11\", \"end\": \"2026-04-28\"},\n        \"cached\": false,\n        \"timestamp\": \"2026-04-29T05:00:00\"\n    }\n}\n```\n\n**Data Sources:**\n- Connected accounts: `x_connections` table (`added` date)\n- Pool/score assignment: `x_account_scores` table (highest score wins)","operationId":"get_ecosystem_creator_growth_api_v2_stats_ecosystem_creator_growth_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EcosystemCreatorGrowthResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/stats/ecosystem-growth":{"get":{"tags":["stats-v2","stats-ecosystem-growth"],"summary":"Get Ecosystem Growth","description":"Retrieve account count over time for a specific ecosystem.\n\nReturns a time series of total accounts and connected (registered) accounts\nper validator run, suitable for a growth chart on ecosystem pages.\n\n**Caching:** 1 hour (3600 seconds)","operationId":"get_ecosystem_growth_api_v2_stats_ecosystem_growth_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"pool","in":"query","required":true,"schema":{"type":"string","description":"Pool/ecosystem name","title":"Pool"},"description":"Pool/ecosystem name"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EcosystemGrowthResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/stats/revenue/summary":{"get":{"tags":["stats-v2","stats-revenue"],"summary":"Get Revenue Summary","description":"Aggregated revenue data for charts.\n\n**Caching:** 6 hours (21,600s)","operationId":"get_revenue_summary_api_v2_stats_revenue_summary_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"interval","in":"query","required":false,"schema":{"type":"string","pattern":"^(monthly|weekly)$","default":"monthly","title":"Interval"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevenueSummaryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/stats/revenue/transactions":{"get":{"tags":["stats-v2","stats-revenue"],"summary":"Get Revenue Transactions","description":"Paginated revenue transaction audit trail.\n\ntx_type='stake' is excluded (duplicate data).","operationId":"get_revenue_transactions_api_v2_stats_revenue_transactions_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"wallet","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Wallet"}},{"name":"tx_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tx Type"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":5000,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevenueTransactionsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/monitoring/chutes-test":{"post":{"tags":["monitoring-v2","monitoring-v2"],"summary":"Test Chutes Performance","description":"Test Chutes AI model performance metrics.\n\nMeasures performance of the MiniMaxAI/MiniMax-M2.1-TEE model:\n- **Time to First Token (TTFT)**: Latency from request to first token\n- **Tokens Per Second**: Average token generation speed\n\n**Parameters (all optional):**\n- **prompt**: The prompt to send to the model (default: \"Tell me a 250 word story.\")\n- **max_tokens**: Maximum tokens to generate (1-4096, default: 1024)\n- **temperature**: Temperature for generation (0.0-2.0, default: 0.7)\n\n**Returns:**\n```json\n{\n    \"model\": \"Qwen/Qwen3-32B\",\n    \"time_to_first_token_ms\": 234.56,\n    \"tokens_per_second\": 45.23,\n    \"total_tokens\": 256,\n    \"total_time_ms\": 5678.90,\n    \"prompt\": \"Tell me a 250 word story.\"\n}\n```\n\n**Errors:**\n- **400**: Invalid request parameters or API key not configured\n- **500**: API request failed or unexpected error\n- **504**: Request timed out (60 second timeout)","operationId":"test_chutes_performance_api_v2_monitoring_chutes_test_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChutesPerformanceRequest","default":{"prompt":"Tell me a 250 word story.","max_tokens":1024,"temperature":0.7}}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChutesPerformanceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/monitoring/twitter-latency":{"post":{"tags":["monitoring-v2","monitoring-v2"],"summary":"Test Twitter Latency","description":"Test Twitter API latency via RapidAPI.\n\nMeasures the response time for fetching user tweets from the Twitter API.\n\n**Parameters (all optional):**\n- **username**: Twitter username to fetch tweets for (default: \"elonmusk\")\n- **limit**: Number of tweets to fetch (1-100, default: 40)\n\n**Returns:**\n```json\n{\n    \"endpoint\": \"twitter-v24.p.rapidapi.com/user/tweets?username=elonmusk&limit=40\",\n    \"latency_ms\": 1234.56,\n    \"status_code\": 200,\n    \"username\": \"elonmusk\",\n    \"limit\": 40,\n    \"success\": true\n}\n```\n\n**Errors:**\n- **400**: Invalid request parameters or API key not configured\n- **500**: API request failed or unexpected error","operationId":"test_twitter_latency_api_v2_monitoring_twitter_latency_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterLatencyRequest","default":{"username":"elonmusk","limit":40}}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwitterLatencyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/monitoring/youtube-latency":{"post":{"tags":["monitoring-v2","monitoring-v2"],"summary":"Test Youtube Latency","description":"Test YouTube Transcriptor API latency via RapidAPI.\n\nMeasures the response time for fetching video transcripts from the YouTube API.\n\n**Parameters (all optional):**\n- **video_id**: YouTube video ID to fetch transcript for (default: \"8aGhZQkoFbQ\")\n- **lang**: Language code for transcript (default: \"en\")\n\n**Returns:**\n```json\n{\n    \"endpoint\": \"youtube-transcriptor.p.rapidapi.com/transcript?video_id=8aGhZQkoFbQ&lang=en\",\n    \"latency_ms\": 987.65,\n    \"status_code\": 200,\n    \"video_id\": \"8aGhZQkoFbQ\",\n    \"lang\": \"en\",\n    \"success\": true\n}\n```\n\n**Errors:**\n- **400**: Invalid request parameters or API key not configured\n- **500**: API request failed or unexpected error","operationId":"test_youtube_latency_api_v2_monitoring_youtube_latency_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/YouTubeLatencyRequest","default":{"video_id":"8aGhZQkoFbQ","lang":"en"}}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/YouTubeLatencyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/monitoring/health":{"get":{"tags":["monitoring-v2","monitoring-v2","health"],"summary":"Performance Health","description":"Health check endpoint for performance monitoring service.\n\nVerifies that the Chutes API key is configured and the service is ready.\n\n**Returns:**\n```json\n{\n    \"status\": \"healthy\",\n    \"service\": \"performance_monitoring\",\n    \"api_key_configured\": true,\n    \"timestamp\": 1642682400.0\n}\n```\n\n**Status Codes:**\n- **200**: Service is healthy and API key is configured\n- **503**: Service is unhealthy or API key not configured","operationId":"performance_health_api_v2_monitoring_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PerformanceHealthResponse"}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/brand/watchlist-tweets":{"get":{"tags":["brand"],"summary":"Watchlist Tweets","description":"Fetch raw tweets for a watchlist handle.\n\nReturns tweets from the watchlist DB + campaign DB (merged & deduped),\nenriched with registered status, ecosystem pools, and sentiment.","operationId":"watchlist_tweets_api_v2_brand_watchlist_tweets_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"handle","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":100,"description":"X/Twitter handle (without @)","title":"Handle"},"description":"X/Twitter handle (without @)"},{"name":"days","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":90,"minimum":7},{"type":"null"}],"description":"Lookback window in days","default":30,"title":"Days"},"description":"Lookback window in days"},{"name":"include_sentiment","in":"query","required":false,"schema":{"type":"boolean","description":"No-op (sentiment always from DB). Kept for backwards compat.","default":true,"title":"Include Sentiment"},"description":"No-op (sentiment always from DB). Kept for backwards compat."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/brand/x-handle-analytics":{"get":{"tags":["brand"],"summary":"X Handle Analytics Legacy","description":"Legacy alias — delegates to /watchlist-tweets.","operationId":"x_handle_analytics_legacy_api_v2_brand_x_handle_analytics_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"handle","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":100,"description":"X/Twitter handle (without @)","title":"Handle"},"description":"X/Twitter handle (without @)"},{"name":"days","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":90,"minimum":7},{"type":"null"}],"description":"Lookback window in days","default":30,"title":"Days"},"description":"Lookback window in days"},{"name":"include_sentiment","in":"query","required":false,"schema":{"type":"boolean","description":"No-op (sentiment always from DB). Kept for backwards compat.","default":true,"title":"Include Sentiment"},"description":"No-op (sentiment always from DB). Kept for backwards compat."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/brand/x-watchlist-group":{"get":{"tags":["brand"],"summary":"X Watchlist Group","description":"Return all watchlist accounts in the same category as the given handle.\n\nThe `belongs_to` field on each account lets the frontend group them\nvisually: parent accounts (empty belongs_to) with their children\n(belongs_to = parent handle), and standalone accounts.\n\nReturns:\n{\n  \"primary\": { \"handle\": \"...\", \"categories\": \"...\", \"belongs_to\": null, ... },\n  \"group\": [\n    { \"handle\": \"...\", \"categories\": \"...\", \"belongs_to\": \"...\", ... },\n    ...\n  ]\n}","operationId":"x_watchlist_group_api_v2_brand_x_watchlist_group_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"handle","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":100,"description":"X/Twitter handle (without @)","title":"Handle"},"description":"X/Twitter handle (without @)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/brand/watchlist":{"get":{"tags":["brand"],"summary":"Get Watchlist","description":"Return the full x_watchlist table grouped by category.\n\nResponse shape:\n{\n  \"accounts\": [ { handle, categories, belongs_to, last_scraped }, ... ],\n  \"by_category\": { \"prediction_market\": [...], \"subnet\": [...], ... },\n  \"categories\": [\"prediction_market\", \"subnet\", ...]\n}","operationId":"get_watchlist_api_v2_brand_watchlist_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}]}},"/api/v2/clients/org":{"get":{"tags":["clients"],"summary":"Get client org config (attention handles + org details)","description":"Returns the org's attention page configuration: which X handles to show\n(the client's own accounts + competitors), with display ordering.","operationId":"get_client_org_config_api_v2_clients_org_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"email","in":"query","required":true,"schema":{"type":"string","description":"Signed-in user's email (org resolution key)","title":"Email"},"description":"Signed-in user's email (org resolution key)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/clients/org/campaigns":{"get":{"tags":["clients"],"summary":"Get campaigns attached to a client org (financials stripped)","description":"Returns all x_briefs linked to this org. Financial data (budget, rewards,\nfees, crypto amounts) is stripped — clients see campaign details and\nperformance only, never pricing.","operationId":"get_client_campaigns_api_v2_clients_org_campaigns_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"email","in":"query","required":true,"schema":{"type":"string","description":"Signed-in user's email (org resolution key)","title":"Email"},"description":"Signed-in user's email (org resolution key)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/clients/org/campaigns/{brief_id}/tweets":{"get":{"tags":["clients"],"summary":"Get matched tweets for a specific campaign (org-scoped)","description":"Returns tweets matched to a campaign, scoped to the caller's org.\nVerifies the campaign belongs to the org before returning data.","operationId":"get_client_campaign_tweets_api_v2_clients_org_campaigns__brief_id__tweets_get","security":[{"APIKeyHeader":[]},{"HTTPBearer":[]}],"parameters":[{"name":"brief_id","in":"path","required":true,"schema":{"type":"integer","title":"Brief Id"}},{"name":"email","in":"query","required":true,"schema":{"type":"string","description":"Signed-in user's email (org resolution key)","title":"Email"},"description":"Signed-in user's email (org resolution key)"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/validator/briefs":{"get":{"tags":["validator"],"summary":"Get Briefs","description":"Retrieve YouTube briefs for validators.\n\nReturns the exact same response format as the standalone briefs-api service.\nNo authentication required.\n\nResponse format: {\"items\": [{id, brief, start_date, end_date, format, boost, cap, prompt_version, ...}]}","operationId":"get_briefs_api_v2_validator_briefs_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v2/validator/x-briefs":{"get":{"tags":["validator"],"summary":"Get X Briefs","description":"Retrieve X/Twitter briefs for validators.\n\nReturns the exact same response format as the standalone briefs-api service.\nNo authentication required.\n\nResponse format: {\"items\": [{id, display, brief, pool, tag, qrt, ...}]}","operationId":"get_x_briefs_api_v2_validator_x_briefs_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v2/validator/pools":{"get":{"tags":["validator"],"summary":"Get Pools","description":"Retrieve X pool configurations for validators.\n\nReturns the exact same response format as the standalone briefs-api service.\nNo authentication required.\n\nResponse format: {\"pools\": [{name, active, keywords, initial_accounts, ...}]}","operationId":"get_pools_api_v2_validator_pools_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v2/auth/google":{"post":{"tags":["auth-v2"],"summary":"Google Auth","description":"Exchange a Google ID token for a Bitcast JWT.\n\nResponses:\n- status=active: User exists and is approved -> token + profile returned.\n- status=pending_approval: User exists but not yet approved -> no token.\n- status=not_registered: No account found for this email -> no token.","operationId":"google_auth_api_v2_auth_google_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoogleAuthRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/auth/register":{"post":{"tags":["auth-v2"],"summary":"Register","description":"Register a new creator account via Google OAuth.","operationId":"register_api_v2_auth_register_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/auth/login":{"post":{"tags":["auth-v2"],"summary":"Email Login","description":"Email/password login.\n\nResponses:\n- status=active: Approved user -> token + profile\n- status=pending_approval: User exists but not approved\n- status=no_password: User exists but was created via OAuth (no password set)\n- 401: Wrong password","operationId":"email_login_api_v2_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailLoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/auth/register-email":{"post":{"tags":["auth-v2"],"summary":"Email Register","description":"Register a new creator account via email/password.","operationId":"email_register_api_v2_auth_register_email_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailRegisterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/auth/forgot-password":{"post":{"tags":["auth-v2"],"summary":"Forgot Password","description":"Request a password reset. Always returns success to avoid email enumeration.\nEmail sending will be added later.","operationId":"forgot_password_api_v2_auth_forgot_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgotPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetTokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/auth/reset-password":{"post":{"tags":["auth-v2"],"summary":"Reset Password","description":"Reset a user's password using a reset token.\nToken validation will be implemented when the token store is added.","operationId":"reset_password_api_v2_auth_reset_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetTokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/auth/refresh":{"post":{"tags":["auth-v2"],"summary":"Refresh Token","description":"Refresh a valid JWT. Returns a new token with extended expiry.","operationId":"refresh_token_api_v2_auth_refresh_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v2/auth/me":{"get":{"tags":["auth-v2"],"summary":"Get Me","description":"Get the current authenticated user's profile.","operationId":"get_me_api_v2_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfile"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v2/auth/impersonate/{user_id}":{"post":{"tags":["auth-v2"],"summary":"Impersonate","description":"Admin-only: Impersonate another portal user.","operationId":"impersonate_api_v2_auth_impersonate__user_id__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImpersonateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/portal/youtube/credentials/upload":{"post":{"tags":["portal-v2","portal-credentials"],"summary":"Upload YouTube credentials (JWT auth)","description":"JWT-authenticated credential upload for the creator portal frontend.\n\nDelegates to the same upload logic as the API-key endpoint, but extracts\nthe portal user ID from the JWT instead of requiring it as a form field.","operationId":"upload_credentials_jwt_api_v2_portal_youtube_credentials_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_credentials_jwt_api_v2_portal_youtube_credentials_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v2/portal/youtube/credentials/connect":{"post":{"tags":["portal-v2","portal-credentials"],"summary":"Connect YouTube via OAuth callback (JWT auth)","description":"Store YouTube channel info from the OAuth callback.\n\nCreates/updates a yt_credentials row with channel name, bitcast_account_id,\nencrypted refresh token, and YPP status. Rejects non-YPP accounts if\nREQUIRE_YPP is enabled.","operationId":"youtube_oauth_connect_api_v2_portal_youtube_credentials_connect_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/YouTubeOAuthConnectRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/YouTubeOAuthConnectResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v2/portal/youtube/credentials/disconnect":{"post":{"tags":["portal-v2","portal-credentials"],"summary":"Disconnect YouTube account (JWT auth)","description":"Disconnect the user's YouTube account.\n\nRevokes the Google OAuth2 refresh token, deletes the yt_credentials row,\nand clears YouTube fields from portal_user.","operationId":"youtube_disconnect_api_v2_portal_youtube_credentials_disconnect_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/YouTubeDisconnectResponse"}}}}},"security":[{"HTTPBearer":[]}]}}},"components":{"schemas":{"AccountStatsResponse":{"properties":{"account_title":{"type":"string","title":"Account Title","description":"YouTube account title from yt_account.title"},"usd_total":{"type":"string","title":"Usd Total","description":"Sum of daily averages for usd_target across all account videos within lookback period (with weight corrections)"},"alpha_total":{"type":"string","title":"Alpha Total","description":"Sum of daily averages for alpha_target across all account videos within lookback period (with weight corrections)"},"daily_earnings":{"type":"string","title":"Daily Earnings","description":"Sum of individual video averages from past 24 hours (only videos in video_matched_to_brief, with weight corrections)"},"cpm":{"type":"string","title":"Cpm","description":"Cost per mille: (total_daily_usd_targets / total_views * 1000) over lookback period"},"total_views":{"type":"integer","title":"Total Views","description":"Total views across all account videos within lookback period"},"outstanding_balance_alpha":{"type":"string","title":"Outstanding Balance Alpha","description":"Outstanding balance in Alpha tokens: sum of alpha_amount for payments with status 'staged' or 'failed'"},"lookback_days_applied":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Lookback Days Applied","description":"Number of lookback days applied to the calculation for transparency (None indicates unlimited)"}},"type":"object","required":["account_title","usd_total","alpha_total","daily_earnings","cpm","total_views","outstanding_balance_alpha","lookback_days_applied"],"title":"AccountStatsResponse","description":"Response model for account-level aggregated statistics."},"AgencyCreator":{"properties":{"id_portal_user":{"type":"integer","title":"Id Portal User"},"email":{"type":"string","title":"Email"},"youtube_channel_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Channel Name"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"last_video_matched_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Video Matched At"},"total_videos_matched":{"type":"integer","title":"Total Videos Matched","default":0},"earnings_7d":{"type":"number","title":"Earnings 7D","default":0.0},"earnings_lifetime":{"type":"number","title":"Earnings Lifetime","default":0.0}},"type":"object","required":["id_portal_user","email"],"title":"AgencyCreator","description":"Creator belonging to an agency."},"AgencyCreatorsResponse":{"properties":{"creators":{"items":{"$ref":"#/components/schemas/AgencyCreator"},"type":"array","title":"Creators"},"total_count":{"type":"integer","title":"Total Count"}},"type":"object","required":["creators","total_count"],"title":"AgencyCreatorsResponse","description":"List of creators belonging to an agency."},"AgencyDailyMetrics":{"properties":{"date":{"type":"string","title":"Date"},"total_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Total Usd"},"total_alpha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Total Alpha"},"creator_earnings_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Creator Earnings Usd"},"agency_earnings_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agency Earnings Usd"}},"type":"object","required":["date"],"title":"AgencyDailyMetrics","description":"Daily earnings breakdown for an agency video."},"AgencyEarningsDaily":{"properties":{"date":{"type":"string","title":"Date"},"agency_earnings_usd":{"type":"string","title":"Agency Earnings Usd"}},"type":"object","required":["date","agency_earnings_usd"],"title":"AgencyEarningsDaily"},"AgencyEarningsMonthly":{"properties":{"month":{"type":"string","title":"Month"},"agency_earnings_usd":{"type":"string","title":"Agency Earnings Usd"}},"type":"object","required":["month","agency_earnings_usd"],"title":"AgencyEarningsMonthly"},"AgencyEarningsResponse":{"properties":{"daily":{"items":{"$ref":"#/components/schemas/AgencyEarningsDaily"},"type":"array","title":"Daily"},"monthly":{"items":{"$ref":"#/components/schemas/AgencyEarningsMonthly"},"type":"array","title":"Monthly"},"total_earned":{"type":"string","title":"Total Earned"},"current_month_earned":{"type":"string","title":"Current Month Earned"}},"type":"object","required":["daily","monthly","total_earned","current_month_earned"],"title":"AgencyEarningsResponse"},"AgencyInfoResponse":{"properties":{"id_agency":{"type":"integer","title":"Id Agency"},"name":{"type":"string","title":"Name"},"referral_code":{"type":"string","title":"Referral Code"},"agency_margin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agency Margin"},"member_count":{"type":"integer","title":"Member Count"}},"type":"object","required":["id_agency","name","referral_code","member_count"],"title":"AgencyInfoResponse","description":"Agency info returned to agency admins."},"AgencyVideoDetailResponse":{"properties":{"video_id":{"type":"string","title":"Video Id"},"title":{"type":"string","title":"Title"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"published_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Published At"},"views":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Views"},"estimated_minutes_watched":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Estimated Minutes Watched"},"estimated_red_partner_revenue":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Estimated Red Partner Revenue"},"brief_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brief Id"},"boost":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Boost"},"format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Format"},"status":{"type":"string","title":"Status","default":"Non-monotized"},"failed_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failed Reason"},"failed_brief_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failed Brief Id"},"usd_total":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usd Total"},"alpha_total":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Alpha Total"},"daily_earnings_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Daily Earnings Usd"},"daily_earnings_alpha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Daily Earnings Alpha"},"creator_earnings_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Creator Earnings Usd"},"creator_earnings_alpha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Creator Earnings Alpha"},"agency_earnings_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agency Earnings Usd"},"agency_earnings_alpha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agency Earnings Alpha"},"daily_metrics":{"items":{"$ref":"#/components/schemas/AgencyDailyMetrics"},"type":"array","title":"Daily Metrics","default":[]},"creator_name":{"type":"string","title":"Creator Name"},"creator_id":{"type":"integer","title":"Creator Id"}},"type":"object","required":["video_id","title","creator_name","creator_id"],"title":"AgencyVideoDetailResponse","description":"Full video detail with agency earnings breakdown."},"AgencyVideoItem":{"properties":{"video_id":{"type":"string","title":"Video Id"},"title":{"type":"string","title":"Title"},"thumbnail_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thumbnail Url"},"published_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Published At"},"total_views":{"type":"integer","title":"Total Views","default":0},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"creator_name":{"type":"string","title":"Creator Name"},"creator_id":{"type":"integer","title":"Creator Id"},"creator_earnings_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Creator Earnings Usd"},"creator_earnings_alpha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Creator Earnings Alpha"},"agency_earnings_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agency Earnings Usd"},"agency_earnings_alpha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agency Earnings Alpha"}},"type":"object","required":["video_id","title","creator_name","creator_id"],"title":"AgencyVideoItem","description":"Video item within an agency context, with two-tier earnings breakdown."},"AgencyVideoListResponse":{"properties":{"videos":{"items":{"$ref":"#/components/schemas/AgencyVideoItem"},"type":"array","title":"Videos"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"limit":{"type":"integer","title":"Limit"},"total_pages":{"type":"integer","title":"Total Pages"},"total_creator_earnings_usd":{"type":"string","title":"Total Creator Earnings Usd"},"total_agency_earnings_usd":{"type":"string","title":"Total Agency Earnings Usd"}},"type":"object","required":["videos","total","page","limit","total_pages","total_creator_earnings_usd","total_agency_earnings_usd"],"title":"AgencyVideoListResponse","description":"Paginated list of videos across all agency creators."},"AggregatedBriefResponse":{"properties":{"aggregated_stats":{"$ref":"#/components/schemas/AggregatedBriefStats","description":"Aggregated statistics across all briefs"},"timeseries_data":{"$ref":"#/components/schemas/AggregatedTimeseriesData","description":"Aggregated historical timeseries data across all briefs"}},"type":"object","required":["aggregated_stats","timeseries_data"],"title":"AggregatedBriefResponse","description":"Response schema for aggregated brief statistics across all briefs."},"AggregatedBriefStats":{"properties":{"total_views":{"type":"integer","title":"Total Views","description":"Total views across all videos from all briefs"},"total_minutes_watched":{"type":"integer","title":"Total Minutes Watched","description":"Total minutes watched across all videos from all briefs"},"video_count":{"type":"integer","title":"Video Count","description":"Total number of videos across all briefs"},"brief_count":{"type":"integer","title":"Brief Count","description":"Total number of briefs included in aggregation"},"latest_data_timestamp":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Latest Data Timestamp","description":"Timestamp of most recent data across all briefs"}},"type":"object","required":["total_views","total_minutes_watched","video_count","brief_count"],"title":"AggregatedBriefStats","description":"Aggregated statistics across all briefs (no brief-specific information)."},"AggregatedStats":{"properties":{"total_views":{"type":"integer","title":"Total Views","description":"Total views across all matched videos"},"total_minutes_watched":{"type":"integer","title":"Total Minutes Watched","description":"Total minutes watched across all matched videos"},"video_count":{"type":"integer","title":"Video Count","description":"Number of videos matched to this brief"},"latest_data_timestamp":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Latest Data Timestamp","description":"Timestamp of most recent data"}},"type":"object","required":["total_views","total_minutes_watched","video_count"],"title":"AggregatedStats","description":"Aggregated statistics for a brief."},"AggregatedTimeseriesData":{"properties":{"views":{"additionalProperties":{"type":"integer"},"type":"object","title":"Views","description":"Daily aggregated views data as date -> count mapping across all briefs"},"minutes_watched":{"additionalProperties":{"type":"integer"},"type":"object","title":"Minutes Watched","description":"Daily aggregated minutes watched as date -> count mapping across all briefs"}},"type":"object","required":["views","minutes_watched"],"title":"AggregatedTimeseriesData","description":"Aggregated timeseries data across all briefs."},"AuthResponse":{"properties":{"token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"},"user":{"anyOf":[{"$ref":"#/components/schemas/UserProfile"},{"type":"null"}]},"status":{"type":"string","title":"Status","description":"active | pending_approval | not_registered | no_password | 2fa_required"},"challenge_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Challenge Token"},"auth_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Auth Provider","description":"Hint: 'google' or 'email' — which provider the account was created with"}},"type":"object","required":["status"],"title":"AuthResponse","description":"Response from auth endpoints (login, register)."},"Body_upload_credentials_api_v1_credentials_upload_post":{"properties":{"credential_file":{"type":"string","format":"binary","title":"Credential File","description":"YouTube credentials JSON file"},"id_portal_user":{"type":"integer","exclusiveMinimum":0.0,"title":"Id Portal User","description":"Portal user ID to associate credentials with"}},"type":"object","required":["credential_file","id_portal_user"],"title":"Body_upload_credentials_api_v1_credentials_upload_post"},"Body_upload_credentials_api_v2_youtube_credentials_credentials_upload_post":{"properties":{"credential_file":{"type":"string","format":"binary","title":"Credential File","description":"YouTube credentials JSON file"},"id_portal_user":{"type":"integer","exclusiveMinimum":0.0,"title":"Id Portal User","description":"Portal user ID to associate credentials with"}},"type":"object","required":["credential_file","id_portal_user"],"title":"Body_upload_credentials_api_v2_youtube_credentials_credentials_upload_post"},"Body_upload_credentials_jwt_api_v2_portal_youtube_credentials_upload_post":{"properties":{"credential_file":{"type":"string","format":"binary","title":"Credential File","description":"YouTube credentials JSON file"}},"type":"object","required":["credential_file"],"title":"Body_upload_credentials_jwt_api_v2_portal_youtube_credentials_upload_post"},"Body_upload_credentials_jwt_api_v2_youtube_credentials_upload_post":{"properties":{"credential_file":{"type":"string","format":"binary","title":"Credential File","description":"YouTube credentials JSON file"}},"type":"object","required":["credential_file"],"title":"Body_upload_credentials_jwt_api_v2_youtube_credentials_upload_post"},"BriefDetailResponse":{"properties":{"brief":{"$ref":"#/components/schemas/BriefResponse","description":"Basic brief information"},"aggregated_stats":{"$ref":"#/components/schemas/AggregatedStats","description":"Aggregated statistics across all matched videos"},"timeseries_data":{"$ref":"#/components/schemas/TimeseriesData","description":"Historical timeseries data"}},"type":"object","required":["brief","aggregated_stats","timeseries_data"],"title":"BriefDetailResponse","description":"Detailed response schema for a single brief with aggregated data."},"BriefFundingResponse":{"properties":{"brief_id":{"type":"string","title":"Brief Id","description":"Brief ID (format: {tag}_{5-digit-hash}, e.g., 'crypto_12345')"},"funding_id":{"type":"integer","title":"Funding Id","description":"Funding record ID"},"start_date":{"type":"string","format":"date","title":"Start Date","description":"Campaign start date (validated)"},"end_date":{"type":"string","format":"date","title":"End Date","description":"Campaign end date (validated)"},"max_tweets":{"type":"integer","title":"Max Tweets","description":"Maximum tweets (validated)"},"usd_reward":{"type":"string","title":"Usd Reward","description":"Reward amount in USD"},"usd_fee":{"type":"string","title":"Usd Fee","description":"Fee amount in USD (25% of reward)"},"usd_total":{"type":"string","title":"Usd Total","description":"Total amount in USD (reward + fee)"},"crypto_amount":{"type":"string","title":"Crypto Amount","description":"Amount in cryptocurrency"},"exchange_rate":{"type":"string","title":"Exchange Rate","description":"Exchange rate (crypto to USD)"},"token":{"type":"string","title":"Token","description":"Token type: 'TAO' or 'USDC'"},"destination_address":{"type":"string","title":"Destination Address","description":"Payment destination address"},"expires":{"type":"string","format":"date-time","title":"Expires","description":"Payment expiration datetime (15 minutes)"},"status":{"type":"string","title":"Status","description":"Funding status: 'pending'"}},"type":"object","required":["brief_id","funding_id","start_date","end_date","max_tweets","usd_reward","usd_fee","usd_total","crypto_amount","exchange_rate","token","destination_address","expires","status"],"title":"BriefFundingResponse","description":"Response schema for funding calculation."},"BriefInfo":{"properties":{"id_brief":{"type":"integer","title":"Id Brief","description":"Internal brief ID"},"brief_id":{"type":"string","title":"Brief Id","description":"Unique brief identifier"},"brief":{"type":"string","title":"Brief","description":"Brief content/description"},"display":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display","description":"Creator-facing brief description"},"start_date":{"type":"string","format":"date","title":"Start Date","description":"Brief start date"},"end_date":{"type":"string","format":"date","title":"End Date","description":"Brief end date"},"format":{"type":"string","title":"Format","description":"Brief format (e.g., adRead, dedicated)"},"boost":{"type":"string","title":"Boost","description":"Brief boost multiplier"},"cap":{"type":"string","title":"Cap","description":"Brief cap value"},"prompt_version":{"type":"integer","title":"Prompt Version","description":"Prompt version number"},"unique_identifier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unique Identifier","description":"Unique identifier if available"},"brand":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Brand","description":"Associated brand ID"},"max_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Count","description":"Maximum count limit for this brief"},"status":{"$ref":"#/components/schemas/BriefStatus","description":"Brief status based on current UTC time vs start/end dates"},"creator_budget":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Creator Budget","description":"Brief budget amount allocated for creators"},"budget_multiplier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Multiplier","description":"Budget multiplier - controls rate of budget drawdown (defaults to 1 if null)"},"remaining_budget":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Remaining Budget","description":"Remaining budget calculated using CPM-based estimation: creator_budget - (total_spend / budget_multiplier), where total_spend is sum of per-video estimates based on creator ARRPV"},"usd_spend":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usd Spend","description":"Total USD spent calculated as: creator_budget - remaining_budget"},"estimated_end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Estimated End Date","description":"Estimated date the campaign budget will run out, based on burn rate"}},"type":"object","required":["id_brief","brief_id","brief","start_date","end_date","format","boost","cap","prompt_version","status"],"title":"BriefInfo","description":"Brief information for videos response including all static fields and calculated budget data."},"BriefResponse":{"properties":{"id_brief":{"type":"integer","title":"Id Brief","description":"Internal brief ID"},"brief_id":{"type":"string","title":"Brief Id","description":"Unique brief identifier"},"brief":{"type":"string","title":"Brief","description":"Brief content/description"},"display":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display","description":"Creator-facing brief description"},"start_date":{"type":"string","format":"date","title":"Start Date","description":"Brief start date"},"end_date":{"type":"string","format":"date","title":"End Date","description":"Brief end date"},"format":{"type":"string","title":"Format","description":"Brief format (e.g., adRead, dedicated)"},"boost":{"type":"string","title":"Boost","description":"Brief boost multiplier"},"cap":{"type":"string","title":"Cap","description":"Brief cap value"},"prompt_version":{"type":"integer","title":"Prompt Version","description":"Prompt version number"},"unique_identifier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unique Identifier","description":"Unique identifier if available"},"brand":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Brand","description":"Associated brand ID"},"budget":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget","description":"Brief budget amount"},"budget_multiplier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Multiplier","description":"Brief budget multiplier"},"max_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Count","description":"Maximum count limit for this brief"},"status":{"$ref":"#/components/schemas/BriefStatus","description":"Brief status based on current UTC time vs start/end dates"},"matched_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Matched Count","description":"Number of this creator's videos matched to this brief"},"earned_amount":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Earned Amount","description":"Total USD earned by this creator on this brief"},"remaining_budget":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Remaining Budget","description":"Remaining budget for this creator on this brief (CPM-based)"},"usd_spend":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usd Spend","description":"Total USD spent by this creator on this brief"},"budget_remaining":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget Remaining","description":"Global remaining budget (CPM-based, same as brief detail page)"},"estimated_end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Estimated End Date","description":"Estimated date the campaign budget will run out, based on burn rate"}},"type":"object","required":["id_brief","brief_id","brief","start_date","end_date","format","boost","cap","prompt_version","status"],"title":"BriefResponse","description":"Response schema for a single brief."},"BriefStatus":{"type":"string","enum":["Upcoming","Live","Complete"],"title":"BriefStatus","description":"Enum for brief status values."},"BriefVideoItem":{"properties":{"bitcast_video_id":{"type":"string","title":"Bitcast Video Id","description":"Unique bitcast video identifier"},"title":{"type":"string","title":"Title","description":"Video title"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At","description":"Video publication timestamp"},"duration":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Duration","description":"Video duration (e.g., 'PT10M30S')"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url","description":"Video URL"},"bitcast_account_id":{"type":"string","title":"Bitcast Account Id","description":"Bitcast account identifier"},"account_title":{"type":"string","title":"Account Title","description":"YouTube account title"},"views":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Views","description":"Latest view count for the video"},"likes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Likes","description":"Latest like count for the video"},"shares":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Shares","description":"Latest share count for the video"}},"type":"object","required":["bitcast_video_id","title","bitcast_account_id","account_title"],"title":"BriefVideoItem","description":"Individual video item in brief videos response."},"BriefVideosResponse":{"properties":{"brief_info":{"$ref":"#/components/schemas/BriefInfo","description":"Basic brief information"},"videos":{"items":{"$ref":"#/components/schemas/BriefVideoItem"},"type":"array","title":"Videos","description":"List of videos matched to this brief"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of videos returned"}},"type":"object","required":["brief_info","videos","total_count"],"title":"BriefVideosResponse","description":"Response schema for brief videos endpoint."},"BriefsListResponse":{"properties":{"briefs":{"items":{"$ref":"#/components/schemas/BriefResponse"},"type":"array","title":"Briefs","description":"List of all briefs"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of briefs"}},"type":"object","required":["briefs","total_count"],"title":"BriefsListResponse","description":"Response schema for list of briefs."},"BulkCreateNotificationLogRequest":{"properties":{"logs":{"items":{"$ref":"#/components/schemas/CreateNotificationLogRequest"},"type":"array","minItems":1,"title":"Logs","description":"Array of log entries to create (1-1000 entries)"}},"type":"object","required":["logs"],"title":"BulkCreateNotificationLogRequest","description":"Request schema for bulk creating notification log entries."},"BulkNotificationLogResponse":{"properties":{"logs":{"items":{"$ref":"#/components/schemas/NotificationLogItem"},"type":"array","title":"Logs","description":"Created log entries"},"total_created":{"type":"integer","title":"Total Created","description":"Number of log entries created"},"queue_processed":{"type":"boolean","title":"Queue Processed","description":"Whether queue was marked as processed"},"queue_fully_logged":{"type":"boolean","title":"Queue Fully Logged","description":"Whether all users have been logged"}},"type":"object","required":["logs","total_created","queue_processed","queue_fully_logged"],"title":"BulkNotificationLogResponse","description":"Response schema for bulk log creation."},"CancelFundingResponse":{"properties":{"funding_id":{"type":"integer","title":"Funding Id","description":"Funding record ID"},"status":{"type":"string","title":"Status","description":"Funding status: 'cancelled'"},"brief_state":{"type":"string","title":"Brief State","description":"Brief version state: 'cancelled'"},"message":{"type":"string","title":"Message","description":"Success message"}},"type":"object","required":["funding_id","status","brief_state","message"],"title":"CancelFundingResponse","description":"Response schema for funding cancellation."},"ChutesPerformanceRequest":{"properties":{"prompt":{"type":"string","title":"Prompt","description":"The prompt to send to the model","default":"Tell me a 250 word story."},"max_tokens":{"type":"integer","maximum":4096.0,"minimum":1.0,"title":"Max Tokens","description":"Maximum number of tokens to generate","default":1024},"temperature":{"type":"number","maximum":2.0,"minimum":0.0,"title":"Temperature","description":"Temperature for response generation","default":0.7}},"type":"object","title":"ChutesPerformanceRequest","description":"Request model for Chutes AI performance testing."},"ChutesPerformanceResponse":{"properties":{"model":{"type":"string","title":"Model","description":"Model identifier that was tested"},"time_to_first_token_ms":{"type":"number","title":"Time To First Token Ms","description":"Time to first token latency in milliseconds"},"tokens_per_second":{"type":"number","title":"Tokens Per Second","description":"Average tokens generated per second"},"total_tokens":{"type":"integer","title":"Total Tokens","description":"Total number of tokens generated"},"total_time_ms":{"type":"number","title":"Total Time Ms","description":"Total time taken for the request in milliseconds"},"prompt":{"type":"string","title":"Prompt","description":"The prompt that was used for testing"}},"type":"object","required":["model","time_to_first_token_ms","tokens_per_second","total_tokens","total_time_ms","prompt"],"title":"ChutesPerformanceResponse","description":"Response model for Chutes AI performance test results."},"ColdkeyRegistrationRequest":{"properties":{"coldkey":{"type":"string","maxLength":50,"minLength":40,"title":"Coldkey","description":"Bittensor substrate wallet address (coldkey)"},"token":{"type":"string","maxLength":10,"title":"Token","description":"Token for no-code coldkey (required)"},"referred_by":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Referred By","description":"Referral information (optional)"},"x_handle":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"X Handle","description":"X/Twitter handle (optional)"}},"type":"object","required":["coldkey","token"],"title":"ColdkeyRegistrationRequest","description":"Request schema for coldkey registration."},"ColdkeyRegistrationResponse":{"properties":{"tag":{"type":"string","title":"Tag","description":"Registration tag for the coldkey"}},"type":"object","required":["tag"],"title":"ColdkeyRegistrationResponse","description":"Response schema for coldkey registration - returns just the tag string.","example":{"tag":"Stitch3-a3c5e8f1"}},"CreateBriefFundingRequest":{"properties":{"brief":{"type":"string","maxLength":2000,"minLength":10,"title":"Brief","description":"Brief description/instructions"},"display":{"type":"string","maxLength":255,"minLength":1,"title":"Display","description":"Display text for brief (user-friendly name)"},"pools":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Pools","description":"List of pool names (e.g., ['elite', 'challenger'])"},"start_date":{"type":"string","format":"date","title":"Start Date","description":"Brief start date (T+1, T+2, or T+3)"},"end_date":{"type":"string","format":"date","title":"End Date","description":"Brief end date (must be start_date + 5)"},"usd_reward":{"anyOf":[{"type":"number","maximum":1000000.0,"minimum":500.0},{"type":"string"}],"title":"Usd Reward","description":"Reward amount in USD"},"token":{"type":"string","title":"Token","description":"Token type: 'TAO' or 'USDC'"},"tag":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tag","description":"Tag/hashtag for brief (used in brief_id generation)"},"qrt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Qrt","description":"Quote retweet URL"},"inclusion_keywords":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inclusion Keywords","description":"Comma-separated secondary keyword inclusion filter (post-search filtering only)"},"max_tweets":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Tweets","description":"Maximum tweets (default: 1, must be 1 if provided)","default":1},"max_members":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Members","description":"Maximum members (default: 150)","default":150},"max_considered":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Considered","description":"Maximum considered (default: 300)","default":300},"discount_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Discount Code","description":"Optional discount code (stored for future use)"},"project":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Project","description":"Project name"},"product":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Product","description":"Product name"},"project_context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Context","description":"Project description or context"},"product_context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Product Context","description":"Product description or context"},"brand_overview":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Brand Overview","description":"Brand overview file name"}},"type":"object","required":["brief","display","pools","start_date","end_date","usd_reward","token"],"title":"CreateBriefFundingRequest","description":"Request schema for creating a brief with funding."},"CreateNotificationLogRequest":{"properties":{"notification_id":{"type":"string","minLength":1,"title":"Notification Id","description":"UUID of notification from queue"},"user_id":{"type":"integer","minimum":1.0,"title":"User Id","description":"Portal user ID"},"success":{"type":"boolean","title":"Success","description":"Whether notification was delivered successfully"},"error_msg":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Msg","description":"Error message if delivery failed"}},"type":"object","required":["notification_id","user_id","success"],"title":"CreateNotificationLogRequest","description":"Request schema for creating notification log entry."},"CreateNotificationQueueRequest":{"properties":{"platform":{"$ref":"#/components/schemas/NotificationPlatform","description":"Target platform (portal/telegram/email)"},"portal_user_ids":{"items":{"type":"integer"},"type":"array","minItems":1,"title":"Portal User Ids","description":"List of user IDs to notify"},"message":{"type":"string","minLength":1,"title":"Message","description":"Notification message content"},"redirect_link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Redirect Link","description":"Optional redirect link"}},"type":"object","required":["platform","portal_user_ids","message"],"title":"CreateNotificationQueueRequest","description":"Request schema for creating notification queue items (DEV/TESTING ONLY).\n\nCreates real notifications without requiring business events like payments or brief creation."},"CreateNotificationQueueResponse":{"properties":{"notification_id":{"type":"string","title":"Notification Id","description":"UUID of created notification"},"platform":{"type":"string","title":"Platform","description":"Target platform"},"portal_user_ids":{"items":{"type":"integer"},"type":"array","title":"Portal User Ids","description":"List of user IDs"},"message":{"type":"string","title":"Message","description":"Notification message"},"redirect_link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Redirect Link","description":"Optional redirect link"},"processed":{"type":"boolean","title":"Processed","description":"Processing status"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Creation timestamp"}},"type":"object","required":["notification_id","platform","portal_user_ids","message","processed","created_at"],"title":"CreateNotificationQueueResponse","description":"Response schema for created queue item."},"CreateUserRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email","description":"Email address for the user"}},"type":"object","required":["email"],"title":"CreateUserRequest","description":"Request schema for creating a new user."},"CreateUserResponse":{"properties":{"id_portal_user":{"type":"integer","title":"Id Portal User","description":"Internal portal user ID"},"email":{"type":"string","title":"Email","description":"User email address"},"mining_coldkey":{"type":"string","title":"Mining Coldkey","description":"Mining coldkey"},"payment_coldkey":{"type":"string","title":"Payment Coldkey","description":"Payment coldkey (empty if not provided)"},"access":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Access","description":"User access permission flag"},"admin":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Admin","description":"User admin permission flag"},"notify_email":{"type":"boolean","title":"Notify Email","description":"Email notification enabled","default":true},"notify_telegram":{"type":"boolean","title":"Notify Telegram","description":"Telegram notification enabled","default":true},"telegram_chat_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Telegram Chat Id","description":"Telegram chat ID for notifications"},"referral_code":{"type":"string","title":"Referral Code","description":"10-character referral code derived from user email"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"User creation timestamp"},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Last update timestamp"},"yt_credentials":{"items":{"$ref":"#/components/schemas/YtCredentialInfo"},"type":"array","title":"Yt Credentials","description":"List of associated YouTube credentials (empty for new users)"}},"type":"object","required":["id_portal_user","email","mining_coldkey","payment_coldkey","referral_code","created_at","updated_at"],"title":"CreateUserResponse","description":"Response schema for creating a user (returns complete user data)."},"CreateWaitlistDetailsRequest":{"properties":{"youtube":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Youtube","description":"YouTube URL or handle"},"x":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"X","description":"X/Twitter URL or handle"},"referred_by":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}],"title":"Referred By","description":"Referral code of the user who referred this user"}},"type":"object","title":"CreateWaitlistDetailsRequest","description":"Request schema for creating/updating waitlist details."},"CreatorGrowthItem":{"properties":{"date":{"type":"string","title":"Date","description":"Date in YYYY-MM-DD format"},"youtube_creators":{"type":"integer","title":"Youtube Creators","description":"Cumulative YouTube creator count"},"x_creators":{"type":"integer","title":"X Creators","description":"Cumulative X/Twitter creator count"},"total":{"type":"integer","title":"Total","description":"Combined cumulative creator count"}},"type":"object","required":["date","youtube_creators","x_creators","total"],"title":"CreatorGrowthItem","description":"Daily creator growth data point."},"CreatorGrowthMeta":{"properties":{"total_youtube":{"type":"integer","title":"Total Youtube","description":"Total YouTube creators"},"total_x":{"type":"integer","title":"Total X","description":"Total X/Twitter creators"},"total_combined":{"type":"integer","title":"Total Combined","description":"Total combined creators"},"cached":{"type":"boolean","title":"Cached","description":"Whether the response was served from cache"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp","description":"Timestamp of when data was retrieved"}},"type":"object","required":["total_youtube","total_x","total_combined","cached","timestamp"],"title":"CreatorGrowthMeta","description":"Metadata for creator growth response."},"CreatorGrowthResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/CreatorGrowthItem"},"type":"array","title":"Data","description":"Daily cumulative creator counts"},"meta":{"$ref":"#/components/schemas/CreatorGrowthMeta","description":"Response metadata"}},"type":"object","required":["data","meta"],"title":"CreatorGrowthResponse","description":"Response schema for creator growth endpoint."},"CredentialHealthResponse":{"properties":{"status":{"type":"string","title":"Status","description":"System health status"},"temp_storage":{"type":"string","title":"Temp Storage","description":"Temporary storage accessibility status"},"permanent_storage":{"type":"string","title":"Permanent Storage","description":"Permanent storage accessibility status"},"max_file_size_mb":{"type":"number","title":"Max File Size Mb","description":"Maximum file size in MB"},"system_ready":{"type":"boolean","title":"System Ready","description":"Whether system is fully operational"}},"type":"object","required":["status","temp_storage","permanent_storage","max_file_size_mb","system_ready"],"title":"CredentialHealthResponse","description":"Credential system health check response."},"CredentialUploadError":{"properties":{"success":{"type":"boolean","title":"Success","description":"Always false for error responses","default":false},"error_type":{"type":"string","title":"Error Type","description":"Type of error (validation, file, api, database, etc.)"},"error_message":{"type":"string","title":"Error Message","description":"Detailed error message"},"error_details":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Error Details","description":"Additional error details"},"timestamp":{"type":"string","title":"Timestamp","description":"When the error occurred (ISO format)"}},"type":"object","required":["error_type","error_message"],"title":"CredentialUploadError","description":"Error response model for credential upload failures."},"CredentialUploadResponse":{"properties":{"success":{"type":"boolean","title":"Success","description":"Whether the upload and validation succeeded"},"message":{"type":"string","title":"Message","description":"Human-readable message about the operation"},"validation_result":{"anyOf":[{"$ref":"#/components/schemas/CredentialValidationResult"},{"type":"null"}],"description":"Detailed validation results"},"file_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"File Name","description":"Name of the validated credential file that was stored"},"processing_time_seconds":{"type":"number","title":"Processing Time Seconds","description":"Time taken to process the upload"}},"type":"object","required":["success","message","processing_time_seconds"],"title":"CredentialUploadResponse","description":"Response model for credential upload operations."},"CredentialValidationResult":{"properties":{"is_valid":{"type":"boolean","title":"Is Valid","description":"Whether credentials are valid overall"},"account_info":{"anyOf":[{"$ref":"#/components/schemas/YouTubeAccountInfo"},{"type":"null"}],"description":"YouTube account information"},"scopes_validated":{"items":{"type":"string"},"type":"array","title":"Scopes Validated","description":"List of successfully validated scopes"},"missing_scopes":{"items":{"type":"string"},"type":"array","title":"Missing Scopes","description":"List of missing required scopes"},"validation_errors":{"items":{"type":"string"},"type":"array","title":"Validation Errors","description":"List of validation error messages"},"validation_timestamp":{"type":"string","title":"Validation Timestamp","description":"When validation was performed (ISO format)"}},"type":"object","required":["is_valid"],"title":"CredentialValidationResult","description":"Result of credential validation process."},"DailyMetrics":{"properties":{"date":{"type":"string","title":"Date","description":"The date for which the metrics are aggregated (ISO format)."},"total_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Total Usd","description":"The total USD earnings for this day."},"total_alpha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Total Alpha","description":"The total alpha earnings for this day."}},"type":"object","required":["date","total_usd","total_alpha"],"title":"DailyMetrics","description":"Daily aggregated metrics for a video."},"DailyViewsDay":{"properties":{"date":{"type":"string","title":"Date","description":"Date in YYYY-MM-DD format"},"tweets":{"items":{"$ref":"#/components/schemas/DailyViewsTweet"},"type":"array","title":"Tweets","description":"Per-tweet views for this day"}},"type":"object","required":["date","tweets"],"title":"DailyViewsDay","description":"One day of stacked tweet views."},"DailyViewsTweet":{"properties":{"tweet_id":{"type":"string","title":"Tweet Id","description":"Internal tweet ID as string"},"username":{"type":"string","title":"Username","description":"Tweet author username"},"content_snippet":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content Snippet","description":"First 80 chars of tweet content"},"views":{"type":"integer","title":"Views","description":"Views for this tweet on this day"}},"type":"object","required":["tweet_id","username","views"],"title":"DailyViewsTweet","description":"Single tweet's views for a given day."},"DatabaseHealthResponse":{"properties":{"database":{"type":"object","title":"Database","description":"Database health information"},"connection_pool":{"type":"object","title":"Connection Pool","description":"Connection pool status and metrics"},"timestamp":{"type":"number","title":"Timestamp","description":"Unix timestamp of health check"}},"type":"object","required":["database","connection_pool","timestamp"],"title":"DatabaseHealthResponse","description":"Detailed database health check response model.\nIncludes connection pool status and metrics."},"EarningsMetrics":{"properties":{"total_tweets":{"type":"integer","title":"Total Tweets","description":"Total number of tweets matched to briefs across all accounts in the pool"},"total_earnings":{"type":"number","title":"Total Earnings","description":"Total earnings in USD (completed/confirmed payments) across all accounts in the pool"},"avg_earnings_per_tweet":{"type":"number","title":"Avg Earnings Per Tweet","description":"Average earnings per tweet in USD across all accounts in the pool"}},"type":"object","required":["total_tweets","total_earnings","avg_earnings_per_tweet"],"title":"EarningsMetrics","description":"Earnings-related metrics for a pool.","example":{"avg_earnings_per_tweet":27.79,"total_earnings":125000.5,"total_tweets":4500}},"EcosystemCreatorGrowthItem":{"properties":{"date":{"type":"string","title":"Date","description":"Date in YYYY-MM-DD format"},"ecosystem":{"type":"string","title":"Ecosystem","description":"Ecosystem/pool name"},"creators":{"type":"integer","title":"Creators","description":"Cumulative connected creator count for this ecosystem"}},"type":"object","required":["date","ecosystem","creators"],"title":"EcosystemCreatorGrowthItem","description":"Daily creator growth data point by ecosystem."},"EcosystemCreatorGrowthMeta":{"properties":{"ecosystems":{"items":{"type":"string"},"type":"array","title":"Ecosystems","description":"List of ecosystem names"},"total_creators":{"type":"integer","title":"Total Creators","description":"Total unique connected creators across all ecosystems"},"date_range":{"type":"object","title":"Date Range","description":"Start and end dates"},"cached":{"type":"boolean","title":"Cached","description":"Whether the response was served from cache"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp","description":"Timestamp of when data was retrieved"}},"type":"object","required":["ecosystems","total_creators","date_range","cached","timestamp"],"title":"EcosystemCreatorGrowthMeta","description":"Metadata for ecosystem creator growth response."},"EcosystemCreatorGrowthResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/EcosystemCreatorGrowthItem"},"type":"array","title":"Data","description":"Daily cumulative creator counts by ecosystem"},"meta":{"$ref":"#/components/schemas/EcosystemCreatorGrowthMeta","description":"Response metadata"}},"type":"object","required":["data","meta"],"title":"EcosystemCreatorGrowthResponse","description":"Response schema for ecosystem creator growth endpoint."},"EcosystemGrowthItem":{"properties":{"date":{"type":"string","title":"Date","description":"Run date in YYYY-MM-DD format"},"total_accounts":{"type":"integer","title":"Total Accounts","description":"Total accounts in ecosystem"}},"type":"object","required":["date","total_accounts"],"title":"EcosystemGrowthItem","description":"Account count snapshot for a single validator run."},"EcosystemGrowthResponse":{"properties":{"pool":{"type":"string","title":"Pool","description":"Pool/ecosystem name"},"data":{"items":{"$ref":"#/components/schemas/EcosystemGrowthItem"},"type":"array","title":"Data","description":"Account counts per validator run"},"cached":{"type":"boolean","title":"Cached","description":"Whether response was served from cache","default":false},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"}},"type":"object","required":["pool"],"title":"EcosystemGrowthResponse","description":"Response schema for ecosystem account growth over time."},"EmailLoginRequest":{"properties":{"email":{"type":"string","title":"Email","description":"User's email"},"password":{"type":"string","title":"Password","description":"User's password"}},"type":"object","required":["email","password"],"title":"EmailLoginRequest","description":"Request body for email/password login."},"EmailRegisterRequest":{"properties":{"email":{"type":"string","title":"Email","description":"User's email"},"password":{"type":"string","minLength":8,"title":"Password","description":"User's password (min 8 chars)"},"youtube_channel_url":{"type":"string","title":"Youtube Channel Url","description":"Creator's YouTube channel URL"},"accept_terms":{"type":"boolean","title":"Accept Terms","description":"Whether the user accepted the terms of service"},"referred_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referred By","description":"Referral code of the user who referred this user"}},"type":"object","required":["email","password","youtube_channel_url","accept_terms"],"title":"EmailRegisterRequest","description":"Request body for email registration."},"FollowerMetrics":{"properties":{"total_followers":{"type":"integer","title":"Total Followers","description":"Sum of all followers in the pool"},"median_followers":{"type":"integer","title":"Median Followers","description":"Median follower count in the pool"}},"type":"object","required":["total_followers","median_followers"],"title":"FollowerMetrics","description":"Follower-related metrics for a pool.","example":{"median_followers":5420,"total_followers":1250000}},"ForgotPasswordRequest":{"properties":{"email":{"type":"string","title":"Email","description":"User's email"}},"type":"object","required":["email"],"title":"ForgotPasswordRequest","description":"Request body for password reset."},"GeographicCountryItem":{"properties":{"country_code":{"type":"string","title":"Country Code","description":"ISO country code (e.g., 'US', 'GB')"},"watch_time_hours":{"type":"number","title":"Watch Time Hours","description":"Total watch time in hours"},"watch_time_minutes":{"type":"integer","title":"Watch Time Minutes","description":"Total watch time in minutes"},"unique_videos":{"type":"integer","title":"Unique Videos","description":"Number of unique videos with views from this country"}},"type":"object","required":["country_code","watch_time_hours","watch_time_minutes","unique_videos"],"title":"GeographicCountryItem","description":"Geographic data for a single country."},"GeographicMeta":{"properties":{"timestamp":{"type":"string","format":"date-time","title":"Timestamp","description":"Timestamp of when data was retrieved"},"total_countries":{"type":"integer","title":"Total Countries","description":"Total number of countries returned"},"cached":{"type":"boolean","title":"Cached","description":"Whether the response was served from cache"}},"type":"object","required":["timestamp","total_countries","cached"],"title":"GeographicMeta","description":"Metadata for geographic response."},"GeographicResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/GeographicCountryItem"},"type":"array","title":"Data","description":"List of countries with watch time data"},"meta":{"$ref":"#/components/schemas/GeographicMeta","description":"Response metadata"}},"type":"object","required":["data","meta"],"title":"GeographicResponse","description":"Response schema for geographic endpoint."},"GoogleAuthRequest":{"properties":{"google_id_token":{"type":"string","title":"Google Id Token","description":"Google ID token from frontend sign-in"}},"type":"object","required":["google_id_token"],"title":"GoogleAuthRequest","description":"Request body for Google OAuth token exchange."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthCheckResponse":{"properties":{"status":{"type":"string","title":"Status","description":"Overall health status (healthy/unhealthy)"},"app_name":{"type":"string","title":"App Name","description":"Application name"},"version":{"type":"string","title":"Version","description":"Application version"},"database":{"type":"object","title":"Database","description":"Database health information"},"timestamp":{"type":"number","title":"Timestamp","description":"Unix timestamp of health check"}},"type":"object","required":["status","app_name","version","database","timestamp"],"title":"HealthCheckResponse","description":"Basic health check response model.\nUsed by root-level health endpoints."},"ImpersonateResponse":{"properties":{"token":{"type":"string","title":"Token"},"user":{"$ref":"#/components/schemas/UserProfile"},"impersonated_by":{"type":"string","title":"Impersonated By"}},"type":"object","required":["token","user","impersonated_by"],"title":"ImpersonateResponse","description":"Response from impersonation endpoint."},"MatchedBriefInfo":{"properties":{"brief_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brief Id","description":"Brief ID"},"format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Format","description":"Brief format"},"boost":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Boost","description":"Boost multiplier"}},"type":"object","title":"MatchedBriefInfo","description":"Info about a brief matched to a video."},"MinerAccessToken":{"properties":{"channel_name":{"type":"string","title":"Channel Name"},"access_token":{"type":"string","title":"Access Token"}},"type":"object","required":["channel_name","access_token"],"title":"MinerAccessToken"},"MinerAccessTokensResponse":{"properties":{"tokens":{"items":{"$ref":"#/components/schemas/MinerAccessToken"},"type":"array","title":"Tokens"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["tokens","total"],"title":"MinerAccessTokensResponse"},"NetworkMetrics":{"properties":{"k_core_10":{"type":"integer","title":"K Core 10","description":"Number of nodes with 10 or more incoming connections"},"top_150_reciprocity":{"type":"number","title":"Top 150 Reciprocity","description":"Percentage of edges that are bidirectional among the top 150 nodes (0-1 range)"},"top_150_edge_density":{"type":"number","title":"Top 150 Edge Density","description":"Edge density among top 150 nodes: actual edges / total possible edges (0-1 range)"}},"type":"object","required":["k_core_10","top_150_reciprocity","top_150_edge_density"],"title":"NetworkMetrics","description":"Network analysis metrics for a pool.","example":{"k_core_10":45,"top_150_edge_density":0.42,"top_150_reciprocity":0.67}},"NotificationLogItem":{"properties":{"id_notification_log":{"type":"integer","title":"Id Notification Log","description":"Log entry ID"},"notification_id":{"type":"string","title":"Notification Id","description":"UUID of notification"},"user_id":{"type":"integer","title":"User Id","description":"Portal user ID"},"success":{"type":"boolean","title":"Success","description":"Delivery success status"},"error_msg":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Msg","description":"Error message if failed"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp","description":"Log entry timestamp"}},"type":"object","required":["id_notification_log","notification_id","user_id","success","timestamp"],"title":"NotificationLogItem","description":"Individual log entry in bulk response."},"NotificationPlatform":{"type":"string","enum":["portal","telegram","email"],"title":"NotificationPlatform","description":"Platform types for notifications."},"NotificationQueueItem":{"properties":{"notification_id":{"type":"string","title":"Notification Id","description":"UUID of notification"},"platform":{"$ref":"#/components/schemas/NotificationPlatform","description":"Target platform"},"portal_user_ids":{"items":{"type":"integer"},"type":"array","title":"Portal User Ids","description":"List of user IDs to notify"},"message":{"type":"string","title":"Message","description":"Notification message content"},"redirect_link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Redirect Link","description":"Optional redirect link"},"processed":{"type":"boolean","title":"Processed","description":"Whether notification has been processed"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Creation timestamp"}},"type":"object","required":["notification_id","platform","portal_user_ids","message","processed","created_at"],"title":"NotificationQueueItem","description":"Response schema for queue item."},"PerformanceBonusBreakdown":{"properties":{"views":{"type":"number","title":"Views","description":"Bonus from views"},"views_per_follower":{"type":"number","title":"Views Per Follower","description":"Bonus from views per follower"},"total_engagements":{"type":"number","title":"Total Engagements","description":"Bonus from total engagements"},"engagement_per_view":{"type":"number","title":"Engagement Per View","description":"Bonus from engagement per view"},"retweet_bonus":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Retweet Bonus","description":"Bonus from retweets"}},"type":"object","required":["views","views_per_follower","total_engagements","engagement_per_view"],"title":"PerformanceBonusBreakdown","description":"Per-metric bonus breakdown for a tweet's performance bonus."},"PerformanceHealthResponse":{"properties":{"status":{"type":"string","title":"Status","description":"Health status (healthy/unhealthy)"},"service":{"type":"string","title":"Service","description":"Service name"},"api_key_configured":{"type":"boolean","title":"Api Key Configured","description":"Whether Chutes API key is configured"},"timestamp":{"type":"number","title":"Timestamp","description":"Unix timestamp of health check"}},"type":"object","required":["status","service","api_key_configured","timestamp"],"title":"PerformanceHealthResponse","description":"Health check response for performance monitoring service."},"PerformanceMetrics":{"properties":{"posts":{"type":"integer","title":"Posts","description":"Total number of posts"},"views":{"type":"integer","title":"Views","description":"Total views across all posts"},"engagements":{"type":"integer","title":"Engagements","description":"Total engagements (sum of favourites, retweets, replies, quotes, bookmarks)"},"engagement_rate":{"type":"number","title":"Engagement Rate","description":"Engagement rate as percentage (engagements/views × 100). Returns 0 if views is 0."},"favourites":{"type":"integer","title":"Favourites","description":"Total favorites/likes"},"retweets":{"type":"integer","title":"Retweets","description":"Total retweets"},"replies":{"type":"integer","title":"Replies","description":"Total replies"},"quotes":{"type":"integer","title":"Quotes","description":"Total quote tweets"},"bookmarks":{"type":"integer","title":"Bookmarks","description":"Total bookmarks"}},"type":"object","required":["posts","views","engagements","engagement_rate","favourites","retweets","replies","quotes","bookmarks"],"title":"PerformanceMetrics","description":"Performance metrics for a brief."},"PlatformReferral":{"properties":{"referral_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Referral Id","description":"Referral ID from x_referral_bonus"},"referee":{"type":"string","title":"Referee","description":"Referee X username"},"referrer":{"type":"string","title":"Referrer","description":"Referrer X username"},"status":{"type":"string","title":"Status","description":"Referral status: pending | completed | paid"},"usd_amount":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usd Amount","description":"USD amount for this referral"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At","description":"When the referral was created"},"referee_uid":{"type":"integer","title":"Referee Uid","description":"Referee user ID"},"referrer_uid":{"type":"integer","title":"Referrer Uid","description":"Referrer user ID"},"referee_amount_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referee Amount Usd","description":"Referee payment amount in USD"},"referrer_amount_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referrer Amount Usd","description":"Referrer payment amount in USD"},"referee_tx_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referee Tx Hash","description":"Referee payment transaction hash"},"referrer_tx_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referrer Tx Hash","description":"Referrer payment transaction hash"},"paid_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Paid At","description":"When payment was made"}},"type":"object","required":["referee","referrer","status","referee_uid","referrer_uid"],"title":"PlatformReferral","description":"Referral with full platform details (admin view)."},"PlatformReferralsResponse":{"properties":{"total_referrals":{"type":"integer","title":"Total Referrals","description":"Total number of referrals","default":0},"completed":{"type":"integer","title":"Completed","description":"Completed referrals","default":0},"pending":{"type":"integer","title":"Pending","description":"Pending referrals","default":0},"total_paid_usd":{"type":"string","title":"Total Paid Usd","description":"Total USD paid out","default":0.0},"referrals":{"items":{"$ref":"#/components/schemas/PlatformReferral"},"type":"array","title":"Referrals","description":"All referrals"}},"type":"object","title":"PlatformReferralsResponse","description":"Response for GET /referrals (platform-wide)."},"PoolConfigItem":{"properties":{"id_x_pools":{"type":"integer","title":"Id X Pools","description":"Pool ID"},"name":{"type":"string","title":"Name","description":"Pool name"},"keywords":{"items":{"type":"string"},"type":"array","title":"Keywords","description":"Keywords to track"},"initial_accounts":{"items":{"type":"string"},"type":"array","title":"Initial Accounts","description":"Initial seed accounts"},"lang":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lang","description":"Language filter (null for all languages)"},"active":{"type":"boolean","title":"Active","description":"Pool active status"},"date_offset":{"type":"integer","title":"Date Offset","description":"Date offset for pool configuration"},"core_min_interaction_weight":{"type":"integer","title":"Core Min Interaction Weight","description":"Minimum interaction weight for core tier"},"core_min_tweets":{"type":"integer","title":"Core Min Tweets","description":"Minimum tweets for core tier"},"core_max_seed_accounts":{"type":"integer","title":"Core Max Seed Accounts","description":"Maximum number of core seed accounts"},"extended_min_interaction_weight":{"type":"integer","title":"Extended Min Interaction Weight","description":"Minimum interaction weight for extended tier"},"extended_min_tweets":{"type":"integer","title":"Extended Min Tweets","description":"Minimum tweets for extended tier"},"extended_max_seed_accounts":{"type":"integer","title":"Extended Max Seed Accounts","description":"Maximum number of extended seed accounts"},"max_discovery_iterations":{"type":"integer","title":"Max Discovery Iterations","description":"Maximum discovery iterations"},"convergence_threshold":{"type":"number","title":"Convergence Threshold","description":"Convergence threshold for discovery"},"max_referral_amount":{"type":"number","title":"Max Referral Amount","description":"Maximum referral reward amount in USD for this pool","default":100.0},"promoted_affiliates":{"items":{"type":"string"},"type":"array","title":"Promoted Affiliates","description":"Account handles manually shortlisted for the pool (guaranteed-include set)"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Pool creation timestamp"},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Pool last update timestamp"},"next_refresh_date":{"type":"string","format":"date","title":"Next Refresh Date","description":"Next pool refresh date (calculated from reference date + offset)"}},"type":"object","required":["id_x_pools","name","keywords","initial_accounts","active","date_offset","core_min_interaction_weight","core_min_tweets","core_max_seed_accounts","extended_min_interaction_weight","extended_min_tweets","extended_max_seed_accounts","max_discovery_iterations","convergence_threshold","created_at","updated_at","next_refresh_date"],"title":"PoolConfigItem","description":"Individual pool configuration item.\n\nContains all fields from the x_pools table."},"PoolConfigsResponse":{"properties":{"pools":{"items":{"$ref":"#/components/schemas/PoolConfigItem"},"type":"array","title":"Pools","description":"List of pool configurations with all x_pools table fields"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of pools returned"}},"type":"object","required":["pools","total_count"],"title":"PoolConfigsResponse","description":"Response schema for pool configurations.\n\nThis is the RECOMMENDED endpoint for getting pool information.\nReturns all x_pools table fields for each pool.","example":{"pools":[{"active":true,"convergence_threshold":0.95,"core_max_seed_accounts":100,"core_min_interaction_weight":5,"core_min_tweets":10,"created_at":"2025-01-01T00:00:00","date_offset":0,"extended_max_seed_accounts":300,"extended_min_interaction_weight":1,"extended_min_tweets":1,"id_x_pools":1,"initial_accounts":["user1","user2"],"keywords":["crypto","blockchain"],"lang":"en","max_discovery_iterations":3,"name":"elite","next_refresh_date":"2026-02-09","updated_at":"2025-01-01T00:00:00"}],"total_count":1}},"PoolMetricsResponse":{"properties":{"pool":{"type":"string","title":"Pool","description":"Pool name"},"account_count":{"type":"integer","title":"Account Count","description":"Total number of accounts in the pool"},"connected_account_count":{"type":"integer","title":"Connected Account Count","description":"Number of accounts in the pool that have a connected X account (record in x_connections)"},"follower_metrics":{"$ref":"#/components/schemas/FollowerMetrics","description":"Follower-related metrics"},"network_metrics":{"$ref":"#/components/schemas/NetworkMetrics","description":"Network analysis metrics"},"earnings_metrics":{"$ref":"#/components/schemas/EarningsMetrics","description":"Earnings-related metrics"}},"type":"object","required":["pool","account_count","connected_account_count","follower_metrics","network_metrics","earnings_metrics"],"title":"PoolMetricsResponse","description":"Response schema for pool metrics.","example":{"account_count":150,"connected_account_count":142,"earnings_metrics":{"avg_earnings_per_tweet":27.79,"total_earnings":125000.5,"total_tweets":4500},"follower_metrics":{"median_followers":5420,"total_followers":1250000},"network_metrics":{"k_core_10":45,"top_150_edge_density":0.42,"top_150_reciprocity":0.67},"pool":"elite"}},"PoolScoreItem":{"properties":{"pool":{"type":"string","title":"Pool","description":"Pool name"},"score":{"type":"string","title":"Score","description":"Account score in this pool (0-1 range)"},"rank":{"type":"integer","title":"Rank","description":"Rank of the account in this pool (1 = highest score)"},"pool_size":{"type":"integer","title":"Pool Size","description":"Total number of accounts in this pool"},"validator_run_date":{"type":"string","format":"date-time","title":"Validator Run Date","description":"Date of the validator run for this pool"}},"type":"object","required":["pool","score","rank","pool_size","validator_run_date"],"title":"PoolScoreItem","description":"Score information for a specific pool."},"PrivateTweetDetail":{"properties":{"id":{"type":"string","title":"Id","description":"Tweet ID as string to preserve precision"},"username":{"type":"string","title":"Username","description":"X/Twitter username/handle of the tweet author"},"reward":{"type":"number","title":"Reward","description":"USD reward for this tweet (0 if no reward)"},"score":{"type":"number","title":"Score","description":"Weight score for this tweet"},"views":{"type":"integer","title":"Views","description":"Number of views"},"engagements":{"type":"integer","title":"Engagements","description":"Total engagements (sum of favourites, retweets, replies, quotes, bookmarks)"},"favourites":{"type":"integer","title":"Favourites","description":"Number of favorites/likes"},"retweets":{"type":"integer","title":"Retweets","description":"Number of retweets"},"replies":{"type":"integer","title":"Replies","description":"Number of replies"},"quotes":{"type":"integer","title":"Quotes","description":"Number of quote tweets"},"bookmarks":{"type":"integer","title":"Bookmarks","description":"Number of bookmarks"},"retweeted_by":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Retweeted By","description":"List of user IDs/handles who retweeted"},"quoted_by":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Quoted By","description":"List of user IDs/handles who quoted"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content","description":"Tweet text content"},"brief_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brief Id","description":"Brief ID this tweet was evaluated against"},"matched":{"type":"boolean","title":"Matched","description":"Whether tweet passed LLM evaluation and matched a brief","default":false},"reasoning":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reasoning","description":"LLM reasoning for match/no-match decision"},"payment_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Status","description":"Payment status (staged/broadcasted/completed/failed)"},"tx_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tx Hash","description":"On-chain transaction hash"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At","description":"Tweet publish date"}},"type":"object","required":["id","username","reward","score","views","engagements","favourites","retweets","replies","quotes","bookmarks"],"title":"PrivateTweetDetail","description":"Tweet detail for private account endpoint, includes brief_id."},"RankHistoryItem":{"properties":{"date":{"type":"string","title":"Date","description":"Date in YYYY-MM-DD format"},"rank":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rank","description":"Rank in the pool (1 = highest score). Null if not in pool."},"pool_size":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pool Size","description":"Total accounts in pool on this date. Null if not in pool."}},"type":"object","required":["date"],"title":"RankHistoryItem","description":"Individual rank entry for a specific date."},"ReferralTargetItem":{"properties":{"username":{"type":"string","title":"Username","description":"Username of the unregistered account"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"Display name"},"followers":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Followers","description":"Follower count"},"connection_strength":{"type":"number","title":"Connection Strength","description":"Smoothed geometric mean of bidirectional relationship weights"},"influence_score":{"type":"number","title":"Influence Score","description":"Influence score in the pool where connection exists"},"referral_reward":{"type":"number","title":"Referral Reward","description":"Calculated referral reward ($0-100) based on followers and influence"},"pool":{"type":"string","title":"Pool","description":"Pool where the connection exists"}},"type":"object","required":["username","connection_strength","influence_score","referral_reward","pool"],"title":"ReferralTargetItem","description":"An unregistered account recommended for referral based on connection strength.","example":{"connection_strength":0.75,"display_name":"Crypto Fan","followers":5000,"influence_score":0.85,"pool":"elite","referral_reward":42,"username":"unregistered_user"}},"ReferralTargetsResponse":{"properties":{"username":{"type":"string","title":"Username","description":"The requesting user's username"},"targets":{"items":{"$ref":"#/components/schemas/ReferralTargetItem"},"type":"array","title":"Targets","description":"Unregistered KOLs sorted by connection strength descending"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of referral targets found","default":0}},"type":"object","required":["username"],"title":"ReferralTargetsResponse","description":"Response for GET /accounts/{username}/referral-targets"},"RegisterRequest":{"properties":{"google_id_token":{"type":"string","title":"Google Id Token","description":"Google ID token from frontend sign-in"},"youtube_channel_url":{"type":"string","title":"Youtube Channel Url","description":"Creator's YouTube channel URL"},"accept_terms":{"type":"boolean","title":"Accept Terms","description":"Whether the user accepted the terms of service"},"referred_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referred By","description":"Referral code of the user who referred this user"}},"type":"object","required":["google_id_token","youtube_channel_url","accept_terms"],"title":"RegisterRequest","description":"Request body for new creator registration."},"ResetPasswordRequest":{"properties":{"token":{"type":"string","title":"Token","description":"Password reset token"},"password":{"type":"string","minLength":8,"title":"Password","description":"New password (min 8 chars)"}},"type":"object","required":["token","password"],"title":"ResetPasswordRequest","description":"Request body for setting a new password after reset."},"ResetTokenResponse":{"properties":{"message":{"type":"string","title":"Message"}},"type":"object","required":["message"],"title":"ResetTokenResponse","description":"Response for password reset endpoints."},"ResolveWalletResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"data":{"items":{"$ref":"#/components/schemas/WalletMatchItem"},"type":"array","title":"Data","description":"Matching creators (empty if none)"}},"type":"object","title":"ResolveWalletResponse","description":"Response schema for resolve-wallet endpoint."},"RevenueSummaryItem":{"properties":{"date":{"type":"string","title":"Date","description":"Date bucket (YYYY-MM-DD or YYYY-Wxx)"},"youtube_tao":{"type":"number","title":"Youtube Tao","description":"YouTube revenue in raw TAO deposits","default":0},"youtube_tao_equivalent":{"type":"number","title":"Youtube Tao Equivalent","description":"YouTube revenue in TAO equivalent (usd/tao_price)","default":0},"youtube_usd":{"type":"number","title":"Youtube Usd","description":"YouTube revenue in USD","default":0},"stitch3_tao":{"type":"number","title":"Stitch3 Tao","description":"Stitch3/X revenue in raw TAO deposits","default":0},"stitch3_tao_equivalent":{"type":"number","title":"Stitch3 Tao Equivalent","description":"Stitch3/X revenue in TAO equivalent","default":0},"stitch3_usd":{"type":"number","title":"Stitch3 Usd","description":"Stitch3/X revenue in USD","default":0},"buyback_tao":{"type":"number","title":"Buyback Tao","description":"Alpha buyback in TAO","default":0},"buyback_alpha":{"type":"number","title":"Buyback Alpha","description":"Alpha buyback in alpha","default":0},"buyback_usd":{"type":"number","title":"Buyback Usd","description":"Alpha buyback in USD","default":0}},"type":"object","required":["date"],"title":"RevenueSummaryItem","description":"Single time-bucket for revenue summary."},"RevenueSummaryMeta":{"properties":{"total_revenue_tao":{"type":"number","title":"Total Revenue Tao","description":"Total revenue in raw TAO deposits","default":0},"total_revenue_tao_equivalent":{"type":"number","title":"Total Revenue Tao Equivalent","description":"Total revenue in TAO equivalent (usd/tao_price)","default":0},"total_revenue_usd":{"type":"number","title":"Total Revenue Usd","description":"Total revenue in USD","default":0},"total_buyback_tao":{"type":"number","title":"Total Buyback Tao","description":"Total buyback in TAO","default":0},"total_buyback_alpha":{"type":"number","title":"Total Buyback Alpha","description":"Total buyback in alpha","default":0},"total_buyback_usd":{"type":"number","title":"Total Buyback Usd","description":"Total buyback in USD","default":0},"buyback_ratio":{"type":"number","title":"Buyback Ratio","description":"Buyback / Revenue ratio","default":0},"interval":{"type":"string","title":"Interval","description":"Aggregation interval","default":"monthly"},"cached":{"type":"boolean","title":"Cached","default":false},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"}},"type":"object","required":["timestamp"],"title":"RevenueSummaryMeta","description":"Metadata for revenue summary response."},"RevenueSummaryResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/RevenueSummaryItem"},"type":"array","title":"Data"},"meta":{"$ref":"#/components/schemas/RevenueSummaryMeta"}},"type":"object","required":["data","meta"],"title":"RevenueSummaryResponse"},"RevenueTransactionItem":{"properties":{"id":{"type":"integer","title":"Id"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"},"tx_type":{"type":"string","title":"Tx Type"},"wallet_address":{"type":"string","title":"Wallet Address"},"amount_tao":{"type":"number","title":"Amount Tao"},"tao_equivalent":{"type":"number","title":"Tao Equivalent","default":0},"alpha_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Alpha Amount"},"usd_value":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Usd Value"},"tx_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tx Hash"},"tao_price_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Tao Price Usd"},"extrinsic_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Extrinsic Id"}},"type":"object","required":["id","timestamp","tx_type","wallet_address","amount_tao"],"title":"RevenueTransactionItem"},"RevenueTransactionsMeta":{"properties":{"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"},"filtered_by":{"type":"object","title":"Filtered By"}},"type":"object","required":["total","limit","offset"],"title":"RevenueTransactionsMeta"},"RevenueTransactionsResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/RevenueTransactionItem"},"type":"array","title":"Data"},"meta":{"$ref":"#/components/schemas/RevenueTransactionsMeta"}},"type":"object","required":["data","meta"],"title":"RevenueTransactionsResponse"},"ScoreChangeItem":{"properties":{"username":{"type":"string","title":"Username","description":"Twitter username"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"X/Twitter display name"},"followers":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Followers","description":"Number of followers"},"connected":{"type":"boolean","title":"Connected","description":"Whether the account is connected (has records in x_connections)"},"current_score":{"type":"number","title":"Current Score","description":"Score in latest validator run (0-1)"},"previous_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Previous Score","description":"Score in previous validator run (null if new)"},"score_change":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Score Change","description":"current_score - previous_score (null if new)"},"rank":{"type":"integer","title":"Rank","description":"Rank in current run (1 = highest score)"},"previous_rank":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Previous Rank","description":"Rank in previous run (null if new)"},"rank_change":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rank Change","description":"previous_rank - current_rank (positive = improved, null if new)"},"is_new":{"type":"boolean","title":"Is New","description":"True if account was not in previous validator run"},"affiliate_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Affiliate Label","description":"Affiliate label text (e.g. 'Polymarket')"},"affiliate_username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Affiliate Username","description":"Affiliate X/Twitter username (e.g. 'polymarket')"},"affiliate_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Affiliate Url","description":"Affiliate deep link URL"},"label_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label Type","description":"Label type (e.g. 'BusinessLabel')"}},"type":"object","required":["username","connected","current_score","rank","is_new"],"title":"ScoreChangeItem","description":"Per-account score delta between two consecutive validator runs."},"ScoreChangesResponse":{"properties":{"pool":{"type":"string","title":"Pool","description":"Pool/ecosystem name"},"current_run_date":{"type":"string","format":"date-time","title":"Current Run Date","description":"Date of current (latest) validator run"},"previous_run_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Previous Run Date","description":"Date of previous validator run (null if only one run exists)"},"accounts":{"items":{"$ref":"#/components/schemas/ScoreChangeItem"},"type":"array","title":"Accounts","description":"Per-account score changes, ordered by current_score desc"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of accounts in current run"},"top_gainers":{"anyOf":[{"items":{"$ref":"#/components/schemas/ScoreChangeItem"},"type":"array"},{"type":"null"}],"title":"Top Gainers","description":"Top 9 accounts with biggest positive rank change, currently in top 25%"},"top_losers":{"anyOf":[{"items":{"$ref":"#/components/schemas/ScoreChangeItem"},"type":"array"},{"type":"null"}],"title":"Top Losers","description":"Top 9 accounts with biggest negative rank change, previously in top 25%"}},"type":"object","required":["pool","current_run_date","total_count"],"title":"ScoreChangesResponse","description":"Response schema for pool score changes between two consecutive validator runs."},"ScriptCheckerHealthResponse":{"properties":{"status":{"type":"string","title":"Status","description":"Health status"},"bitcast_api_available":{"type":"boolean","title":"Bitcast Api Available","description":"Whether bitcast internal API is accessible"},"response_time_ms":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Response Time Ms","description":"Response time in milliseconds"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Error message if unhealthy"}},"type":"object","required":["status","bitcast_api_available"],"title":"ScriptCheckerHealthResponse","description":"Response schema for script checker service health check."},"ScriptCheckerRequest":{"properties":{"brief_id":{"type":"string","title":"Brief Id","description":"Brief ID to check script against. Use 'custom' to supply a brief body directly."},"transcript":{"type":"string","maxLength":70000,"title":"Transcript","description":"Video transcript (max 70,000 characters)"},"description":{"type":"string","maxLength":10000,"title":"Description","description":"Brief description/content (max 10,000 characters)"},"duration":{"type":"string","title":"Duration","description":"Video duration (formats: '8:27', '00:08:27', or '507' seconds)"},"brief_body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brief Body","description":"Brief content to use directly. Required when brief_id is 'custom'."}},"type":"object","required":["brief_id","transcript","description","duration"],"title":"ScriptCheckerRequest","description":"Request schema for script checking."},"ScriptCheckerResponse":{"properties":{"brief_id":{"type":"string","title":"Brief Id","description":"Brief ID that was checked"},"match":{"type":"boolean","title":"Match","description":"Whether the script content matches the brief requirements"},"reasoning":{"type":"string","title":"Reasoning","description":"Detailed reasoning for the checking result"}},"type":"object","required":["brief_id","match","reasoning"],"title":"ScriptCheckerResponse","description":"Response schema for script checking results."},"ServiceHealthResponse":{"properties":{"status":{"type":"string","title":"Status","description":"Service health status"},"service":{"type":"string","title":"Service","description":"Service name"},"database_accessible":{"type":"boolean","title":"Database Accessible","description":"Whether database is accessible"},"timestamp":{"type":"number","title":"Timestamp","description":"Unix timestamp of health check"}},"type":"object","required":["status","service","database_accessible","timestamp"],"title":"ServiceHealthResponse","description":"Generic service-level health check response.\nUsed by individual service health endpoints."},"SingleVideoResponse":{"properties":{"title":{"type":"string","title":"Title","description":"Video title"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url","description":"YouTube video URL"},"views":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Views","description":"Total view count for the video."},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At","description":"Video publication date"},"daily_earnings_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Daily Earnings Usd","description":"Average usd_target over validator runs in past 24 hours from request time"},"daily_earnings_alpha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Daily Earnings Alpha","description":"Average alpha_target over validator runs in past 24 hours from request time"},"usd_total":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usd Total","description":"Sum of daily averages for usd_target across entire video history"},"alpha_total":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Alpha Total","description":"Sum of daily averages for alpha_target across entire video history"},"daily_metrics":{"items":{"$ref":"#/components/schemas/DailyMetrics"},"type":"array","title":"Daily Metrics","description":"Time-series of daily earnings."},"brief_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brief Id","description":"Brief ID if video is matched to a brief, null otherwise"},"boost":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Boost","description":"Brief boost multiplier (from matched brief), null if no brief matched"},"format":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Format","description":"Brief format (e.g., 'adRead', 'dedicated') from matched brief, null if no brief matched"},"estimated_minutes_watched":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Estimated Minutes Watched","description":"Estimated minutes watched from the latest metrics run."},"estimated_red_partner_revenue":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Estimated Red Partner Revenue","description":"Estimated Red Partner Revenue from the latest metrics run."},"status":{"type":"string","title":"Status","description":"Video monetization status: 'Earning' (matched to brief, ≤3 weeks old), 'Complete' (matched to brief, >3 weeks old), 'Non-monotized' (not matched to brief)"},"failed_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failed Reason","description":"LLM reasoning for why the video failed evaluation"},"failed_brief_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failed Brief Id","description":"Brief ID the video was evaluated against when it failed"},"matched_briefs":{"items":{"$ref":"#/components/schemas/MatchedBriefInfo"},"type":"array","title":"Matched Briefs","description":"All briefs matched to this video"}},"type":"object","required":["title","url","views","published_at","daily_earnings_usd","daily_earnings_alpha","usd_total","alpha_total","daily_metrics","brief_id","boost","format","estimated_minutes_watched","estimated_red_partner_revenue","status"],"title":"SingleVideoResponse","description":"Response model for a single video with detailed metrics."},"SubmitPaymentRequest":{"properties":{"tx_hash":{"type":"string","minLength":10,"title":"Tx Hash","description":"Transaction hash"},"block_number":{"type":"integer","exclusiveMinimum":0.0,"title":"Block Number","description":"Block number where transaction was confirmed"},"sender":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sender","description":"Sender wallet address"}},"type":"object","required":["tx_hash","block_number"],"title":"SubmitPaymentRequest","description":"Request schema for submitting payment transaction hash and block number."},"SubmitPaymentResponse":{"properties":{"brief_id":{"type":"string","title":"Brief Id","description":"Brief ID"},"funding_id":{"type":"integer","title":"Funding Id","description":"Funding record ID"},"version":{"type":"integer","title":"Version","description":"Brief version number"},"status":{"type":"string","title":"Status","description":"Payment status: 'complete'"},"brief_state":{"type":"string","title":"Brief State","description":"Brief state: 'active'"},"message":{"type":"string","title":"Message","description":"Success message"}},"type":"object","required":["brief_id","funding_id","version","status","brief_state","message"],"title":"SubmitPaymentResponse","description":"Response schema for payment submission."},"TimeseriesData":{"properties":{"views":{"additionalProperties":{"type":"integer"},"type":"object","title":"Views","description":"Daily views data as date -> count mapping"},"minutes_watched":{"additionalProperties":{"type":"integer"},"type":"object","title":"Minutes Watched","description":"Daily minutes watched as date -> count mapping"}},"type":"object","required":["views","minutes_watched"],"title":"TimeseriesData","description":"Timeseries data for a brief."},"TimeseriesMetrics":{"properties":{"posts":{"additionalProperties":{"type":"integer"},"type":"object","title":"Posts","description":"Daily post count by date"},"views":{"additionalProperties":{"type":"integer"},"type":"object","title":"Views","description":"Daily views by date"},"engagements":{"additionalProperties":{"type":"integer"},"type":"object","title":"Engagements","description":"Daily engagements by date (sum of favourites, retweets, replies, quotes, bookmarks)"},"favorites":{"additionalProperties":{"type":"integer"},"type":"object","title":"Favorites","description":"Daily favorites by date"},"retweets":{"additionalProperties":{"type":"integer"},"type":"object","title":"Retweets","description":"Daily retweets by date"},"quotes":{"additionalProperties":{"type":"integer"},"type":"object","title":"Quotes","description":"Daily quotes by date"},"replies":{"additionalProperties":{"type":"integer"},"type":"object","title":"Replies","description":"Daily replies by date"},"bookmarks":{"additionalProperties":{"type":"integer"},"type":"object","title":"Bookmarks","description":"Daily bookmarks by date"}},"type":"object","required":["posts","views","engagements","favorites","retweets","quotes","replies","bookmarks"],"title":"TimeseriesMetrics","description":"Time-series engagement metrics."},"TokenResponse":{"properties":{"token":{"type":"string","title":"Token"},"expires_at":{"type":"string","title":"Expires At"}},"type":"object","required":["token","expires_at"],"title":"TokenResponse","description":"Response from token refresh."},"TopConnectionItem":{"properties":{"username":{"type":"string","title":"Username","description":"Username of the connected account"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"Display name of the connected account"},"pool":{"type":"string","title":"Pool","description":"Pool where this connection exists"},"connection_strength":{"type":"number","title":"Connection Strength","description":"Smoothed geometric mean of bidirectional relationship weights - one-way connections have low but non-zero scores"},"influence_score":{"type":"number","title":"Influence Score","description":"Influence score of the connected account in this pool"}},"type":"object","required":["username","pool","connection_strength","influence_score"],"title":"TopConnectionItem","description":"A top connection for an account based on bidirectional relationship strength.","example":{"connection_strength":0.75,"influence_score":0.92,"pool":"elite","username":"connected_user"}},"TopUpBriefFundingRequest":{"properties":{"usd_reward":{"anyOf":[{"type":"number","exclusiveMinimum":0.0},{"type":"string"}],"title":"Usd Reward","description":"Additional reward amount in USD (any positive amount)"},"token":{"type":"string","title":"Token","description":"Token type: 'TAO' or 'USDC'"},"discount_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Discount Code","description":"Optional discount code (stored for future use)"},"end_date":{"type":"string","format":"date","title":"End Date","description":"Updated end date (must be start_date + 5)"},"max_tweets":{"type":"integer","title":"Max Tweets","description":"Updated max tweets (must be 1)"}},"type":"object","required":["usd_reward","token","end_date","max_tweets"],"title":"TopUpBriefFundingRequest","description":"Request schema for topping up an existing brief."},"TweetDetail":{"properties":{"id":{"type":"string","title":"Id","description":"Tweet ID as string to preserve precision"},"username":{"type":"string","title":"Username","description":"X/Twitter username/handle of the tweet author"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"Display name of the tweet author"},"reward":{"type":"number","title":"Reward","description":"USD reward for this tweet (0 if no reward)"},"score":{"type":"number","title":"Score","description":"Weight score for this tweet"},"views":{"type":"integer","title":"Views","description":"Number of views"},"engagements":{"type":"integer","title":"Engagements","description":"Total engagements (sum of favourites, retweets, replies, quotes, bookmarks)"},"favourites":{"type":"integer","title":"Favourites","description":"Number of favorites/likes"},"retweets":{"type":"integer","title":"Retweets","description":"Number of retweets"},"replies":{"type":"integer","title":"Replies","description":"Number of replies"},"quotes":{"type":"integer","title":"Quotes","description":"Number of quote tweets"},"bookmarks":{"type":"integer","title":"Bookmarks","description":"Number of bookmarks"},"retweeted_by":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Retweeted By","description":"List of user IDs/handles who retweeted (JSON array)"},"quoted_by":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Quoted By","description":"List of user IDs/handles who quoted (JSON array)"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content","description":"Tweet text content"},"author_influence":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Author Influence","description":"Author's influence score from social map"},"baseline_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Baseline Score","description":"Baseline score (author_influence × 2)"},"score_breakdown":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Score Breakdown","description":"Engagement contributions: [{u: username, t: rt|qt, i: influence, s: cabal_scale, c: contribution}]"},"performance_bonus_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Performance Bonus Pct","description":"Total performance bonus percentage (up to 10%)"},"performance_bonus_breakdown":{"anyOf":[{"$ref":"#/components/schemas/PerformanceBonusBreakdown"},{"type":"null"}],"description":"Per-metric bonus breakdown: {views, views_per_follower, total_engagements, engagement_per_view, retweet_bonus}"},"is_featured_tweet":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Featured Tweet","description":"Whether this tweet is the featured tweet for the campaign"},"payment_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Status","description":"Payment status: completed, failed, pending"},"tx_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tx Hash","description":"On-chain transaction hash for payment"}},"type":"object","required":["id","username","reward","score","views","engagements","favourites","retweets","replies","quotes","bookmarks"],"title":"TweetDetail","description":"Individual tweet with all its metrics and data."},"TwitterAccountDetailResponse":{"properties":{"username":{"type":"string","title":"Username","description":"Twitter username/handle"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"X/Twitter display name"},"bitcast_account_id":{"type":"string","title":"Bitcast Account Id","description":"Bitcast account ID"},"followers":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Followers","description":"Number of followers for the account"},"connected":{"type":"boolean","title":"Connected","description":"Whether the account is connected (has records in x_connections)"},"pool_scores":{"items":{"$ref":"#/components/schemas/PoolScoreItem"},"type":"array","title":"Pool Scores","description":"Scores for each pool the account is a member of"},"highest_score":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Highest Score","description":"Highest score across all pools (0-1 range)"},"top_connections":{"items":{"$ref":"#/components/schemas/TopConnectionItem"},"type":"array","title":"Top Connections","description":"Top 18 connections ranked by bidirectional relationship strength (smoothed geometric mean - one-way connections have low but non-zero scores)"},"payment_wallet":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Wallet","description":"Connected payment wallet address (coldkey from no-code registration)"},"mining_wallet":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mining Wallet","description":"Mining wallet coldkey resolved via hotkey tag + metagraph (fallback when no payment_wallet)"},"referral_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referral Code","description":"Referral code for the account (Base64url-encoded username)"},"referral_reward":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Referral Reward","description":"Referral reward amount in USD based on followers and highest influence score"},"affiliate_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Affiliate Label","description":"Affiliate label text (e.g. 'Polymarket')"},"affiliate_username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Affiliate Username","description":"Affiliate X/Twitter username (e.g. 'polymarket')"},"affiliate_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Affiliate Url","description":"Affiliate deep link URL"},"label_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label Type","description":"Label type (e.g. 'BusinessLabel')"}},"type":"object","required":["username","bitcast_account_id","connected"],"title":"TwitterAccountDetailResponse","description":"Response schema for a single Twitter account's details.","example":{"bitcast_account_id":"bc_account_123","connected":true,"followers":1250,"highest_score":0.95432,"payment_wallet":"5GHqJxPX5aYqKMT7JNaZJcKmMbNRpP8JCYHqJxPX5aYqKMT7","pool_scores":[{"pool":"elite","pool_size":150,"rank":3,"score":0.95432,"validator_run_date":"2025-12-08T10:30:00"},{"pool":"challenger","pool_size":200,"rank":12,"score":0.87654,"validator_run_date":"2025-12-08T10:30:00"}],"referral_code":"Y3J5cHRvdXNlcjE","top_connections":[{"connection_strength":0.85,"influence_score":0.92,"pool":"elite","username":"connected_user1"},{"connection_strength":0.72,"influence_score":0.88,"pool":"elite","username":"connected_user2"}],"username":"cryptouser1"}},"TwitterAccountPrivateResponse":{"properties":{"username":{"type":"string","title":"Username","description":"Twitter username/handle"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"X/Twitter display name"},"total_tweets":{"type":"integer","title":"Total Tweets","description":"Total tweets matched to briefs"},"total_earnings":{"type":"number","title":"Total Earnings","description":"Total earnings in USD (completed/confirmed payments)"},"avg_earned_per_tweet":{"type":"number","title":"Avg Earned Per Tweet","description":"Average earnings per tweet in USD"},"payment_wallet":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Wallet","description":"Connected coldkey wallet address from no-code registration"},"mining_wallet":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mining Wallet","description":"Mining wallet coldkey resolved via hotkey tag + metagraph (fallback when no payment_wallet)"},"rank_history":{"additionalProperties":{"items":{"$ref":"#/components/schemas/RankHistoryItem"},"type":"array"},"type":"object","title":"Rank History","description":"Rank history grouped by pool (up to 1 year)"},"earnings_per_day":{"additionalProperties":{"type":"number"},"type":"object","title":"Earnings Per Day","description":"Daily earnings in USD for past 12 months (YYYY-MM-DD: amount)"},"recent_tweets":{"items":{"$ref":"#/components/schemas/PrivateTweetDetail"},"type":"array","title":"Recent Tweets","description":"Last 30 tweets matched with briefs"}},"type":"object","required":["username","total_tweets","total_earnings","avg_earned_per_tweet"],"title":"TwitterAccountPrivateResponse","description":"Response schema for private Twitter account details (logged-in user data).","example":{"avg_earned_per_tweet":27.79,"earnings_per_day":{"2025-12-07":75.0,"2025-12-08":50.25},"payment_wallet":"5GHqJxPX5aYqKMT7JNaZJcKmMbNRpP8JCYHqJxPX5aYqKMT7","rank_history":{"elite":[{"date":"2025-12-08","pool_size":150,"rank":3},{"date":"2025-12-01","pool_size":148,"rank":5}]},"recent_tweets":[{"bookmarks":15,"brief_id":"campaign_xyz","content":"Tweet text here...","engagements":208,"favourites":120,"id":"1234567890","published_at":"2025-12-08T10:30:00","quoted_by":[],"quotes":5,"replies":23,"retweeted_by":[],"retweets":45,"reward":25.5,"score":0.85,"username":"cryptouser1","views":5000}],"total_earnings":1250.5,"total_tweets":45,"username":"cryptouser1"}},"TwitterAccountScoreItem":{"properties":{"username":{"type":"string","title":"Username","description":"Twitter username"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"X/Twitter display name"},"bitcast_account_id":{"type":"string","title":"Bitcast Account Id","description":"Bitcast account ID"},"connected":{"type":"boolean","title":"Connected","description":"Whether the account is connected (has records in x_connections)"},"followers":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Followers","description":"Number of followers for the account"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category","description":"Account category (e.g. kol, pm_exchange, bittensor_subnet)"},"affiliate_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Affiliate Label","description":"Affiliate label text (e.g. 'Polymarket')"},"affiliate_username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Affiliate Username","description":"Affiliate X/Twitter username (e.g. 'polymarket')"},"affiliate_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Affiliate Url","description":"Affiliate deep link URL"},"label_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label Type","description":"Label type (e.g. 'BusinessLabel')"},"score":{"type":"string","title":"Score","description":"Account score (0-1 range)"},"validator_run_date":{"type":"string","format":"date-time","title":"Validator Run Date","description":"Date of the validator run"}},"type":"object","required":["username","bitcast_account_id","connected","score","validator_run_date"],"title":"TwitterAccountScoreItem","description":"Individual Twitter account score item."},"TwitterAccountScoresResponse":{"properties":{"pool":{"type":"string","title":"Pool","description":"Pool name queried"},"query_date":{"type":"string","title":"Query Date","description":"Date queried (YYYY-MM-DD)"},"validator_run_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Validator Run Date","description":"Date of validator run used (null if no run found)"},"scores":{"items":{"$ref":"#/components/schemas/TwitterAccountScoreItem"},"type":"array","title":"Scores","description":"List of account scores"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of scores returned (after applying limit if specified)"},"total_before_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Before Limit","description":"Total number of scores available before applying limit (null if no limit was applied)"},"adjacency_matrix":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Adjacency Matrix","description":"Adjacency matrix data for the pool (JSON)"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Optional message (e.g., when no validator run found)"}},"type":"object","required":["pool","query_date","total_count"],"title":"TwitterAccountScoresResponse","description":"Response schema for Twitter account scores by pool and date."},"TwitterAllAccountsItem":{"properties":{"username":{"type":"string","title":"Username","description":"Twitter username/handle"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"X/Twitter display name"},"bitcast_account_id":{"type":"string","title":"Bitcast Account Id","description":"Bitcast account ID"},"scores":{"additionalProperties":{"type":"number"},"type":"object","title":"Scores","description":"Dictionary of pool names to scores, sorted by score descending (0-1 range)"},"followers":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Followers","description":"Number of followers for the account"},"connected":{"type":"boolean","title":"Connected","description":"Whether the account is connected (has records in x_connections)"},"referral_reward":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Referral Reward","description":"Referral reward amount in USD based on followers and highest influence score"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category","description":"Account category (e.g., kol, pm_exchange, news)"},"affiliate_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Affiliate Label","description":"Affiliate label text (e.g. 'Polymarket')"},"affiliate_username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Affiliate Username","description":"Affiliate X/Twitter username (e.g. 'polymarket')"},"affiliate_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Affiliate Url","description":"Affiliate deep link URL"},"label_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label Type","description":"Label type (e.g. 'BusinessLabel')"}},"type":"object","required":["username","bitcast_account_id","scores","connected"],"title":"TwitterAllAccountsItem","description":"Individual Twitter account item across all pools."},"TwitterAllAccountsResponse":{"properties":{"accounts":{"items":{"$ref":"#/components/schemas/TwitterAllAccountsItem"},"type":"array","title":"Accounts","description":"List of all accounts"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of unique accounts"},"validator_run_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Validator Run Date","description":"Date of validator run used (null if no run found)"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Optional message (e.g., when no validator run found)"}},"type":"object","required":["total_count"],"title":"TwitterAllAccountsResponse","description":"Response schema for all Twitter accounts across all pools.","example":{"accounts":[{"bitcast_account_id":"bc_account_123","connected":true,"followers":1250,"scores":{"challenger":0.87654,"elite":0.95432},"username":"cryptouser1"}],"total_count":1,"validator_run_date":"2025-12-08T10:30:00"}},"TwitterBriefAnalyticsResponse":{"properties":{"brief_id":{"type":"string","title":"Brief Id","description":"Unique brief identifier"},"timeseries_metrics":{"$ref":"#/components/schemas/TimeseriesMetrics","description":"Time-series engagement data"},"data_timestamp":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Data Timestamp","description":"Timestamp of most recent data"}},"type":"object","required":["brief_id","timeseries_metrics"],"title":"TwitterBriefAnalyticsResponse","description":"Response for GET /api/v1/twitter/briefs/{brief_id}/analytics"},"TwitterBriefDetailResponse":{"properties":{"brief_id":{"type":"string","title":"Brief Id","description":"Unique brief identifier"},"display":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display","description":"Creator-facing brief description (falls back to brief if null)"},"brief":{"type":"string","title":"Brief","description":"Brief description/content"},"budget":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget","description":"Brief budget amount"},"max_tweets":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Tweets","description":"Maximum number of tweets allowed for this brief"},"max_members":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Members","description":"Maximum number of members allowed to participate in this brief"},"max_considered":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Considered","description":"Maximum number of tweets to consider during validation"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date","description":"Brief start date"},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date","description":"Brief end date"},"status":{"anyOf":[{"$ref":"#/components/schemas/TwitterBriefStatus"},{"type":"null"}],"description":"Brief status"},"pools":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Pools","description":"All pools associated with this brief (JSON array)"},"tag":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tag","description":"Tag associated with this brief"},"qrt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Qrt","description":"QRT (Quote Retweet) URL or identifier for this brief"},"inclusion_keywords":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inclusion Keywords","description":"Comma-separated secondary keyword inclusion filter (post-search filtering only)"},"performance_metrics":{"$ref":"#/components/schemas/PerformanceMetrics","description":"Aggregated performance metrics from most recent validator run"},"tweets":{"items":{"$ref":"#/components/schemas/TweetDetail"},"type":"array","title":"Tweets","description":"List of tweets with their metrics, ordered by weight DESC"}},"type":"object","required":["brief_id","brief","performance_metrics","tweets"],"title":"TwitterBriefDetailResponse","description":"Response for GET /api/v1/twitter/briefs/{brief_id}"},"TwitterBriefListItem":{"properties":{"brief_id":{"type":"string","title":"Brief Id","description":"Unique brief identifier"},"display":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display","description":"Creator-facing brief description (falls back to brief if null)"},"brief":{"type":"string","title":"Brief","description":"Brief description/name"},"Posts":{"type":"integer","title":"Posts","description":"Number of tweets currently matched to this brief (from most recent validator run)"},"views":{"type":"integer","title":"Views","description":"Total views across all tweets in this brief (from most recent validator run)"},"budget":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Budget","description":"Brief budget amount"},"max_tweets":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Tweets","description":"Maximum number of tweets allowed for this brief"},"max_members":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Members","description":"Maximum number of members allowed to participate in this brief"},"max_considered":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Considered","description":"Maximum number of tweets to consider during validation"},"start_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Start Date","description":"Brief start date"},"end_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"End Date","description":"Brief end date"},"status":{"anyOf":[{"$ref":"#/components/schemas/TwitterBriefStatus"},{"type":"null"}],"description":"Brief status (Upcoming/Live/Complete)"},"pools":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Pools","description":"All pools associated with this brief (JSON array)"},"tag":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tag","description":"Tag associated with this brief"},"qrt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Qrt","description":"QRT (Quote Retweet) URL or identifier for this brief"},"inclusion_keywords":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inclusion Keywords","description":"Comma-separated secondary keyword inclusion filter (post-search filtering only)"},"matched_by_user":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Matched By User","description":"Number of tweets matched by the requesting user (only when username filter is provided)"}},"type":"object","required":["brief_id","brief","Posts","views"],"title":"TwitterBriefListItem","description":"Individual Twitter brief in list response."},"TwitterBriefStatus":{"type":"string","enum":["Upcoming","Live","Complete"],"title":"TwitterBriefStatus","description":"Enum for Twitter brief status values."},"TwitterBriefsListResponse":{"properties":{"briefs":{"items":{"$ref":"#/components/schemas/TwitterBriefListItem"},"type":"array","title":"Briefs","description":"List of all Twitter briefs"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of briefs"},"page":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page","description":"Current page number"},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Limit","description":"Items per page"},"total_pages":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Pages","description":"Total number of pages"}},"type":"object","required":["briefs","total_count"],"title":"TwitterBriefsListResponse","description":"Response for GET /api/v1/twitter/briefs"},"TwitterDailySummaryItem":{"properties":{"date":{"type":"string","format":"date","title":"Date","description":"Date of the summary"},"tweets":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Tweets","description":"Number of tweets on this date"},"views":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Views","description":"Total views on this date"},"retweets":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Retweets","description":"Total retweets on this date"},"quotes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Quotes","description":"Total quotes on this date"},"likes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Likes","description":"Total likes on this date"},"bookmarks":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Bookmarks","description":"Total bookmarks on this date"},"replies":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Replies","description":"Total replies on this date"},"registrations":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Registrations","description":"Number of registrations on this date"},"total_followers":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Followers","description":"Total followers across all connected creators on this date"},"usd_earnings":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usd Earnings","description":"USD earnings on this date"},"alpha_earnings":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Alpha Earnings","description":"Alpha earnings on this date"}},"type":"object","required":["date"],"title":"TwitterDailySummaryItem","description":"Single day's Twitter/X daily summary data."},"TwitterDailySummaryResponse":{"properties":{"daily_summaries":{"items":{"$ref":"#/components/schemas/TwitterDailySummaryItem"},"type":"array","title":"Daily Summaries","description":"List of daily summaries"},"total":{"$ref":"#/components/schemas/TwitterTotalAggregation","description":"Aggregated totals across all returned days"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of days returned"}},"type":"object","required":["daily_summaries","total","total_count"],"title":"TwitterDailySummaryResponse","description":"Response schema for Twitter daily summary endpoint."},"TwitterLatencyRequest":{"properties":{"username":{"type":"string","title":"Username","description":"Twitter username to fetch tweets for","default":"elonmusk"},"limit":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Limit","description":"Number of tweets to fetch","default":40}},"type":"object","title":"TwitterLatencyRequest","description":"Request model for Twitter API latency testing."},"TwitterLatencyResponse":{"properties":{"endpoint":{"type":"string","title":"Endpoint","description":"API endpoint that was tested"},"latency_ms":{"type":"number","title":"Latency Ms","description":"Total request latency in milliseconds"},"status_code":{"type":"integer","title":"Status Code","description":"HTTP response status code"},"username":{"type":"string","title":"Username","description":"Twitter username queried"},"limit":{"type":"integer","title":"Limit","description":"Number of tweets requested"},"success":{"type":"boolean","title":"Success","description":"Whether the request was successful"}},"type":"object","required":["endpoint","latency_ms","status_code","username","limit","success"],"title":"TwitterLatencyResponse","description":"Response model for Twitter API latency test results."},"TwitterPlatformStatsResponse":{"properties":{"avg_earned_per_tweet":{"type":"number","title":"Avg Earned Per Tweet","description":"All-time average USD earned per tweet across the entire platform (only tweets with a reward > 0 are included)"},"top_earning_tweet_usd":{"type":"number","title":"Top Earning Tweet Usd","description":"All-time highest USD amount ever earned by a single tweet"}},"type":"object","required":["avg_earned_per_tweet","top_earning_tweet_usd"],"title":"TwitterPlatformStatsResponse","description":"Response schema for platform-wide all-time Twitter stats.","example":{"avg_earned_per_tweet":27.79,"top_earning_tweet_usd":850.0}},"TwitterPoolsResponse":{"properties":{"pools":{"items":{"type":"string"},"type":"array","title":"Pools","description":"List of available pool names (LEGACY - use /twitter/pool-configs endpoint for full details)"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of pools"}},"type":"object","required":["pools","total_count"],"title":"TwitterPoolsResponse","description":"Response schema for available Twitter pools.\n\nDEPRECATED: This schema only returns pool names. \nUse PoolConfigsResponse instead for complete pool information.","deprecated":true,"example":{"pools":["elite","challenger","rising"],"total_count":3}},"TwitterTotalAggregation":{"properties":{"total_views":{"type":"integer","title":"Total Views","description":"Sum of all views/impressions"},"total_likes":{"type":"integer","title":"Total Likes","description":"Sum of all likes"},"total_retweets":{"type":"integer","title":"Total Retweets","description":"Sum of all retweets"},"total_quotes":{"type":"integer","title":"Total Quotes","description":"Sum of all quotes"},"total_bookmarks":{"type":"integer","title":"Total Bookmarks","description":"Sum of all bookmarks"},"total_replies":{"type":"integer","title":"Total Replies","description":"Sum of all replies"},"total_tweets":{"type":"integer","title":"Total Tweets","description":"Sum of all tweets"},"total_registrations":{"type":"integer","title":"Total Registrations","description":"Sum of all registrations"},"max_total_followers":{"type":"integer","title":"Max Total Followers","description":"Maximum total followers across all days","default":0},"total_usd_earnings":{"type":"string","title":"Total Usd Earnings","description":"Sum of all USD earnings"},"total_alpha_earnings":{"type":"string","title":"Total Alpha Earnings","description":"Sum of all Alpha earnings"},"unique_days":{"type":"integer","title":"Unique Days","description":"Number of days with data"}},"type":"object","required":["total_views","total_likes","total_retweets","total_quotes","total_bookmarks","total_replies","total_tweets","total_registrations","total_usd_earnings","total_alpha_earnings","unique_days"],"title":"TwitterTotalAggregation","description":"Aggregated totals for Twitter daily summary data."},"UnprocessedQueueResponse":{"properties":{"notifications":{"items":{"$ref":"#/components/schemas/NotificationQueueItem"},"type":"array","title":"Notifications","description":"List of unprocessed notifications"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of unprocessed notifications"}},"type":"object","required":["notifications","total_count"],"title":"UnprocessedQueueResponse","description":"Response schema for unprocessed queue items."},"UpdateAccessRequest":{"properties":{"access":{"type":"boolean","title":"Access","description":"Access permission flag (true=approved, false=revoked)"}},"type":"object","required":["access"],"title":"UpdateAccessRequest","description":"Request schema for updating user access."},"UpdateAccessResponse":{"properties":{"id_portal_user":{"type":"integer","title":"Id Portal User","description":"Portal user ID"},"email":{"type":"string","title":"Email","description":"User email address"},"access":{"type":"boolean","title":"Access","description":"Updated access permission flag"},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Timestamp when access was updated"}},"type":"object","required":["id_portal_user","email","access","updated_at"],"title":"UpdateAccessResponse","description":"Response schema for user access update."},"UpdateArchiveRequest":{"properties":{"archived":{"type":"boolean","title":"Archived","description":"Archive flag (true=archived, false=unarchived)"}},"type":"object","required":["archived"],"title":"UpdateArchiveRequest","description":"Request schema for archiving/unarchiving a user."},"UpdateArchiveResponse":{"properties":{"id_portal_user":{"type":"integer","title":"Id Portal User","description":"Portal user ID"},"email":{"type":"string","title":"Email","description":"User email address"},"archived":{"type":"boolean","title":"Archived","description":"Updated archive flag"},"archived_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Archived At","description":"Timestamp when user was archived"},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Timestamp when archive status was updated"}},"type":"object","required":["id_portal_user","email","archived","updated_at"],"title":"UpdateArchiveResponse","description":"Response schema for user archive update."},"UpdateInactiveRequest":{"properties":{"inactive":{"type":"boolean","title":"Inactive","description":"Inactive flag (true=inactive, false=active)"}},"type":"object","required":["inactive"],"title":"UpdateInactiveRequest","description":"Request schema for marking a creator as inactive/active."},"UpdateInactiveResponse":{"properties":{"id_portal_user":{"type":"integer","title":"Id Portal User","description":"Portal user ID"},"email":{"type":"string","title":"Email","description":"User email address"},"inactive":{"type":"boolean","title":"Inactive","description":"Updated inactive flag"},"inactive_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Inactive At","description":"Timestamp when creator was marked inactive"},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Timestamp when inactive status was updated"}},"type":"object","required":["id_portal_user","email","inactive","updated_at"],"title":"UpdateInactiveResponse","description":"Response schema for creator inactive status update."},"UpdateNotificationPreferencesRequest":{"properties":{"notify_email":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Notify Email","description":"Enable/disable email notifications"},"notify_telegram":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Notify Telegram","description":"Enable/disable telegram notifications"}},"type":"object","title":"UpdateNotificationPreferencesRequest","description":"Request schema for updating notification preferences."},"UpdatePaymentColdkeyRequest":{"properties":{"payment_coldkey":{"type":"string","title":"Payment Coldkey","description":"Payment coldkey to set for the user"}},"type":"object","required":["payment_coldkey"],"title":"UpdatePaymentColdkeyRequest","description":"Request schema for updating a user's payment coldkey."},"UpdatePaymentColdkeyResponse":{"properties":{"id_portal_user":{"type":"integer","title":"Id Portal User","description":"Portal user ID"},"payment_coldkey":{"type":"string","title":"Payment Coldkey","description":"Updated payment coldkey"},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Timestamp when payment coldkey was updated"}},"type":"object","required":["id_portal_user","payment_coldkey","updated_at"],"title":"UpdatePaymentColdkeyResponse","description":"Response schema for payment coldkey update."},"UpdateTelegramChatIdRequest":{"properties":{"telegram_chat_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Telegram Chat Id","description":"Telegram chat ID to set for the user (null to clear)"}},"type":"object","title":"UpdateTelegramChatIdRequest","description":"Request schema for updating a user's Telegram chat ID."},"UpdateTelegramChatIdResponse":{"properties":{"id_portal_user":{"type":"integer","title":"Id Portal User","description":"Portal user ID"},"telegram_chat_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Telegram Chat Id","description":"Updated Telegram chat ID"},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Timestamp when Telegram chat ID was updated"}},"type":"object","required":["id_portal_user","updated_at"],"title":"UpdateTelegramChatIdResponse","description":"Response schema for Telegram chat ID update."},"UserDetailsResponse":{"properties":{"id_portal_user":{"type":"integer","title":"Id Portal User","description":"Internal portal user ID"},"email":{"type":"string","title":"Email","description":"User email address"},"mining_coldkey":{"type":"string","title":"Mining Coldkey","description":"Mining coldkey"},"payment_coldkey":{"type":"string","title":"Payment Coldkey","description":"Payment coldkey (empty if not provided)"},"access":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Access","description":"User access permission flag"},"admin":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Admin","description":"User admin permission flag"},"notify_email":{"type":"boolean","title":"Notify Email","description":"Email notification enabled","default":true},"notify_telegram":{"type":"boolean","title":"Notify Telegram","description":"Telegram notification enabled","default":true},"telegram_chat_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Telegram Chat Id","description":"Telegram chat ID for notifications"},"referral_code":{"type":"string","title":"Referral Code","description":"10-character referral code derived from user email"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"User creation timestamp"},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Last update timestamp"},"yt_credentials":{"items":{"$ref":"#/components/schemas/YtCredentialInfo"},"type":"array","title":"Yt Credentials","description":"List of associated YouTube credentials"},"youtube_channel_avatar":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Channel Avatar","description":"YouTube channel avatar URL"},"is_agency_admin":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Agency Admin","description":"Whether user is an agency admin"},"agency_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Agency Id","description":"FK to agencies table"},"platform_margin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Platform Margin","description":"Platform margin as decimal"},"agency_margin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agency Margin","description":"Agency margin as decimal"}},"type":"object","required":["id_portal_user","email","mining_coldkey","payment_coldkey","referral_code","created_at","updated_at"],"title":"UserDetailsResponse","description":"Response schema for user details including YT credentials."},"UserListResponse":{"properties":{"users":{"items":{"$ref":"#/components/schemas/UserSummary"},"type":"array","title":"Users","description":"List of user summaries (ID and email)"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of users returned"}},"type":"object","required":["users","total_count"],"title":"UserListResponse","description":"Response schema for users list endpoint."},"UserProfile":{"properties":{"id":{"type":"integer","title":"Id"},"email":{"type":"string","title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"youtube_channel":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Channel"},"youtube_channel_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Channel Id"},"youtube_channel_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Channel Name"},"youtube_channel_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Channel Handle"},"youtube_channel_avatar":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Channel Avatar"},"mining_coldkey":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mining Coldkey"},"payment_coldkey":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Coldkey"},"is_admin":{"type":"boolean","title":"Is Admin","default":false},"is_approved":{"type":"boolean","title":"Is Approved","default":false},"has_youtube_connected":{"type":"boolean","title":"Has Youtube Connected","default":false},"notify_email":{"type":"boolean","title":"Notify Email","default":true},"notify_telegram":{"type":"boolean","title":"Notify Telegram","default":true},"referral_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referral Code"},"referred_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referred By"},"is_agency_admin":{"type":"boolean","title":"Is Agency Admin","default":false},"agency_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Agency Id"},"has_password":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Password","description":"Whether the user has a password set (False for OAuth-only accounts)"},"platform_margin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Platform Margin"},"agency_margin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agency Margin"}},"type":"object","required":["id","email"],"title":"UserProfile","description":"User profile returned in auth responses."},"UserReferral":{"properties":{"referral_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Referral Id","description":"Referral ID from x_referral_bonus"},"referee":{"type":"string","title":"Referee","description":"Referee X username"},"referrer":{"type":"string","title":"Referrer","description":"Referrer X username"},"status":{"type":"string","title":"Status","description":"Referral status: pending | completed | paid"},"usd_amount":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usd Amount","description":"USD amount for this referral"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At","description":"When the referral was created"},"role":{"type":"string","title":"Role","description":"User's role: referee | referrer"},"tx_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tx Hash","description":"Payment transaction hash"},"paid_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Paid At","description":"When payment was made"},"estimated_reward":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Estimated Reward","description":"Estimated referral reward (0-100) for pending referrals"},"locked":{"type":"boolean","title":"Locked","description":"Whether the reward amount is locked at registration","default":false}},"type":"object","required":["referee","referrer","status","role"],"title":"UserReferral","description":"Single referral from the perspective of the requesting user."},"UserReferralsResponse":{"properties":{"referral_code":{"type":"string","title":"Referral Code","description":"User's referral code"},"referrals":{"items":{"$ref":"#/components/schemas/UserReferral"},"type":"array","title":"Referrals","description":"User's referrals"},"total_pending":{"type":"integer","title":"Total Pending","description":"Number of pending referrals","default":0},"total_completed":{"type":"integer","title":"Total Completed","description":"Number of completed referrals","default":0},"total_paid":{"type":"integer","title":"Total Paid","description":"Number of paid referrals","default":0},"total_earned_usd":{"type":"string","title":"Total Earned Usd","description":"Total USD earned from referrals","default":0.0}},"type":"object","required":["referral_code"],"title":"UserReferralsResponse","description":"Response for GET /referrals/me."},"UserSummary":{"properties":{"id_portal_user":{"type":"integer","title":"Id Portal User","description":"Internal portal user ID"},"email":{"type":"string","title":"Email","description":"User email address"},"access":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Access","description":"User access permission flag"},"admin":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Admin","description":"User admin permission flag"},"is_agency_admin":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Agency Admin","description":"Whether user is an agency admin"},"notify_email":{"type":"boolean","title":"Notify Email","description":"Email notification enabled","default":true},"notify_telegram":{"type":"boolean","title":"Notify Telegram","description":"Telegram notification enabled","default":true},"telegram_chat_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Telegram Chat Id","description":"Telegram chat ID for notifications"},"youtube_channel_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Channel Name","description":"YouTube channel name"},"waitlist_youtube":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Waitlist Youtube","description":"YouTube URL from waitlist signup"},"waitlist_x":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Waitlist X","description":"X/Twitter handle from waitlist signup"},"referral_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referral Code","description":"User's own referral code"},"referred_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referred By","description":"Referral code of user who referred this user"},"agency_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agency Name","description":"Name of the agency the user was referred by (if referred_by matches an agency referral code)"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At","description":"Date the user joined"},"last_video_matched_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Video Matched At","description":"Date of last video matched to brief"},"total_videos_matched":{"type":"integer","title":"Total Videos Matched","description":"Total count of distinct videos matched to briefs","default":0},"earnings_7d":{"type":"number","title":"Earnings 7D","description":"USD earnings in the past 7 days (weight-corrected)","default":0.0},"earnings_lifetime":{"type":"number","title":"Earnings Lifetime","description":"Lifetime USD earnings (weight-corrected)","default":0.0},"youtube_connected":{"type":"boolean","title":"Youtube Connected","description":"True if user has a connected YouTube channel (not just a waitlist entry)","default":false},"archived":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Archived","description":"Whether the user has been archived","default":false},"archived_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Archived At","description":"Timestamp when user was archived"},"inactive":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Inactive","description":"Whether the creator is inactive","default":false},"inactive_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Inactive At","description":"Timestamp when creator was marked inactive"}},"type":"object","required":["id_portal_user","email"],"title":"UserSummary","description":"Schema for user summary information (ID, email, access, and admin)."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VideoHealthResponse":{"properties":{"service":{"type":"string","title":"Service","description":"Service name"},"status":{"type":"string","title":"Status","description":"Service health status"},"timestamp":{"type":"string","title":"Timestamp","description":"ISO timestamp of health check"},"database":{"type":"object","title":"Database","description":"Database connectivity and response time information"}},"type":"object","required":["service","status","timestamp","database"],"title":"VideoHealthResponse","description":"Video service health check response with database metrics."},"VideoItem":{"properties":{"id_video":{"type":"integer","title":"Id Video","description":"Internal video ID"},"bitcast_video_id":{"type":"string","title":"Bitcast Video Id","description":"Bitcast unique video identifier"},"title":{"type":"string","title":"Title","description":"Video title"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At","description":"Video publication date"},"brief_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brief Id","description":"Brief ID if video is matched to a brief, null otherwise"},"daily_earnings_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Daily Earnings Usd","description":"Average usd_target over validator runs in past 24 hours from request time"},"daily_earnings_alpha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Daily Earnings Alpha","description":"Average alpha_target over validator runs in past 24 hours from request time"},"usd_total":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usd Total","description":"Sum of daily averages for usd_target across entire video history"},"alpha_total":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Alpha Total","description":"Sum of daily averages for alpha_target across entire video history"},"cpm":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cpm","description":"Cost per mille (CPM): cost per 1000 views calculated as (usd_total / views) * 1000"},"status":{"type":"string","title":"Status","description":"Video monetization status: 'Earning' (matched to brief, ≤3 weeks old), 'Complete' (matched to brief, >3 weeks old), 'Non-monotized' (not matched to brief), 'failed' (passed prescreen but failed LLM check)"},"failed_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failed Reason","description":"LLM reasoning for why the video failed evaluation"},"failed_brief_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Failed Brief Id","description":"Brief ID the video was evaluated against when it failed"},"video_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Video Id","description":"YouTube video ID"},"matched_briefs":{"items":{"$ref":"#/components/schemas/MatchedBriefInfo"},"type":"array","title":"Matched Briefs","description":"All briefs matched to this video"}},"type":"object","required":["id_video","bitcast_video_id","title","published_at","brief_id","daily_earnings_usd","daily_earnings_alpha","usd_total","alpha_total","cpm","status"],"title":"VideoItem","description":"Individual video item with all required fields."},"VideoListResponse":{"properties":{"videos":{"items":{"$ref":"#/components/schemas/VideoItem"},"type":"array","title":"Videos","description":"List of user videos with calculated metrics"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of videos returned"},"date_lookback_applied":{"type":"integer","title":"Date Lookback Applied","description":"Date lookback filter applied in days"}},"type":"object","required":["videos","total_count","date_lookback_applied"],"title":"VideoListResponse","description":"Response model for video list API."},"WaitlistDetailsResponse":{"properties":{"id_user_waitlist_details":{"type":"integer","title":"Id User Waitlist Details","description":"ID of the waitlist details record"},"portal_user_id":{"type":"integer","title":"Portal User Id","description":"Portal user ID"},"youtube":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube","description":"YouTube handle"},"x":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X","description":"X/Twitter handle"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Record creation timestamp"},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Last update timestamp"}},"type":"object","required":["id_user_waitlist_details","portal_user_id","created_at","updated_at"],"title":"WaitlistDetailsResponse","description":"Response schema for waitlist details operations."},"WalletMatchItem":{"properties":{"username":{"type":"string","title":"Username","description":"Creator username/handle"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"Display name"},"payment_wallet_abbr":{"type":"string","title":"Payment Wallet Abbr","description":"First 5 chars of the wallet for confirmation"},"platform":{"type":"string","title":"Platform","description":"Platform: 'youtube' or 'x'"},"registered":{"type":"boolean","title":"Registered","description":"Whether the account has completed registration (has active connections)","default":true}},"type":"object","required":["username","payment_wallet_abbr","platform"],"title":"WalletMatchItem","description":"Single wallet match result."},"XAccountDistributionItem":{"properties":{"x_account_id":{"type":"integer","title":"X Account Id","description":"X account ID"},"bitcast_account_id":{"type":"string","title":"Bitcast Account Id","description":"Bitcast account identifier"},"uid":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Uid","description":"Chain UID from miner table (subnet 93)"},"total_usd":{"type":"number","title":"Total Usd","description":"Sum of usd_target from latest validator run"},"total_weight":{"type":"number","title":"Total Weight","description":"Sum of weight from latest validator run","default":0.0},"total_alpha":{"type":"number","title":"Total Alpha","description":"Sum of alpha_target from latest validator run","default":0.0},"tweet_count":{"type":"integer","title":"Tweet Count","description":"Number of tweets matched to briefs"}},"type":"object","required":["x_account_id","bitcast_account_id","total_usd","tweet_count"],"title":"XAccountDistributionItem","description":"Individual X account distribution from latest validator run."},"XAccountEarningsItem":{"properties":{"x_account_id":{"type":"integer","title":"X Account Id","description":"X account ID"},"username":{"type":"string","title":"Username","description":"X/Twitter username"},"bitcast_account_id":{"type":"string","title":"Bitcast Account Id","description":"Bitcast account identifier"},"followers":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Followers","description":"Number of followers"},"is_connected":{"type":"boolean","title":"Is Connected","description":"Whether account has a record in x_connections"},"total_tweets":{"type":"integer","title":"Total Tweets","description":"Total tweets matched to briefs"},"total_earnings":{"type":"number","title":"Total Earnings","description":"Total USD earnings (completed/confirmed payments)"},"avg_earned_per_tweet":{"type":"number","title":"Avg Earned Per Tweet","description":"Average earnings per tweet in USD"}},"type":"object","required":["x_account_id","username","bitcast_account_id","is_connected","total_tweets","total_earnings","avg_earned_per_tweet"],"title":"XAccountEarningsItem","description":"Individual X account earnings item."},"XAllAccountsEarningsResponse":{"properties":{"accounts":{"items":{"$ref":"#/components/schemas/XAccountEarningsItem"},"type":"array","title":"Accounts","description":"List of X accounts with earnings"},"totals":{"$ref":"#/components/schemas/XEarningsTotals","description":"Aggregated earnings totals"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of accounts with earnings"}},"type":"object","required":["accounts","totals","total_count"],"title":"XAllAccountsEarningsResponse","description":"Response schema for all X accounts earnings endpoint."},"XDistributionResponse":{"properties":{"accounts":{"items":{"$ref":"#/components/schemas/XAccountDistributionItem"},"type":"array","title":"Accounts","description":"List of X accounts with earnings breakdown"},"totals":{"$ref":"#/components/schemas/XDistributionTotals","description":"Aggregated totals"},"timeframe":{"type":"string","title":"Timeframe","description":"Timeframe filter applied ('30d' or 'all')"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of accounts"}},"type":"object","required":["accounts","totals","timeframe","total_count"],"title":"XDistributionResponse","description":"Response schema for X distribution endpoint."},"XDistributionTotals":{"properties":{"total_usd":{"type":"number","title":"Total Usd","description":"Sum of all usd_target"},"total_accounts":{"type":"integer","title":"Total Accounts","description":"Total accounts with earnings"},"total_tweets":{"type":"integer","title":"Total Tweets","description":"Total tweets matched to briefs","default":0},"total_weight":{"type":"number","title":"Total Weight","description":"Sum of all weight","default":0.0},"total_alpha":{"type":"number","title":"Total Alpha","description":"Sum of all alpha","default":0.0}},"type":"object","required":["total_usd","total_accounts"],"title":"XDistributionTotals","description":"Aggregated totals for X distribution."},"XEarningsTotals":{"properties":{"total_usd":{"type":"number","title":"Total Usd","description":"Sum of all USD earnings"},"total_accounts_with_earnings":{"type":"integer","title":"Total Accounts With Earnings","description":"Total accounts with earnings > $0"},"total_accounts_connected":{"type":"integer","title":"Total Accounts Connected","description":"Connected accounts with earnings"},"total_accounts_not_connected":{"type":"integer","title":"Total Accounts Not Connected","description":"Unconnected accounts with earnings"},"connected_usd":{"type":"number","title":"Connected Usd","description":"USD earnings from connected accounts"},"not_connected_usd":{"type":"number","title":"Not Connected Usd","description":"USD earnings from unconnected accounts"},"total_tweets_matched":{"type":"integer","title":"Total Tweets Matched","description":"Total tweets matched to briefs across all accounts"}},"type":"object","required":["total_usd","total_accounts_with_earnings","total_accounts_connected","total_accounts_not_connected","connected_usd","not_connected_usd","total_tweets_matched"],"title":"XEarningsTotals","description":"Aggregated totals for X earnings."},"YouTubeAccountInfo":{"properties":{"account_name":{"type":"string","title":"Account Name","description":"YouTube channel/account name"},"channel_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Channel Id","description":"YouTube channel ID"},"is_ypp":{"type":"boolean","title":"Is Ypp","description":"Whether account has YouTube Partner Program access"},"verification_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Verification Status","description":"Channel verification status"},"subscriber_count":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subscriber Count","description":"Number of subscribers (may be 'hidden' if private)"}},"type":"object","required":["account_name","is_ypp"],"title":"YouTubeAccountInfo","description":"Information about a YouTube account extracted from credentials."},"YouTubeDailySummaryItem":{"properties":{"date":{"type":"string","format":"date","title":"Date","description":"Date of the summary"},"videos_published":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Videos Published","description":"Number of videos published on this date"},"views":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Views","description":"Total views on this date"},"minutes_watched":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Minutes Watched","description":"Total minutes watched on this date"},"shares":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Shares","description":"Total shares on this date"},"likes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Likes","description":"Total likes on this date"},"usd_earnings":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usd Earnings","description":"USD earnings on this date"},"alpha_earnings":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Alpha Earnings","description":"Alpha earnings on this date"},"accounts_connected":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Accounts Connected","description":"Number of accounts connected on this date"},"total_subscribers":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Subscribers","description":"Total subscribers on this date"}},"type":"object","required":["date"],"title":"YouTubeDailySummaryItem","description":"Single day's YouTube daily summary data."},"YouTubeDailySummaryResponse":{"properties":{"daily_summaries":{"items":{"$ref":"#/components/schemas/YouTubeDailySummaryItem"},"type":"array","title":"Daily Summaries","description":"List of daily summaries"},"total":{"$ref":"#/components/schemas/YouTubeTotalAggregation","description":"Aggregated totals across all returned days"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of days returned"}},"type":"object","required":["daily_summaries","total","total_count"],"title":"YouTubeDailySummaryResponse","description":"Response schema for YouTube daily summary endpoint."},"YouTubeDisconnectResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"message":{"type":"string","title":"Message"}},"type":"object","required":["success","message"],"title":"YouTubeDisconnectResponse","description":"Response confirming YouTube disconnection."},"YouTubeLatencyRequest":{"properties":{"video_id":{"type":"string","title":"Video Id","description":"YouTube video ID to fetch transcript for","default":"8aGhZQkoFbQ"},"lang":{"type":"string","title":"Lang","description":"Language code for transcript","default":"en"}},"type":"object","title":"YouTubeLatencyRequest","description":"Request model for YouTube Transcriptor API latency testing."},"YouTubeLatencyResponse":{"properties":{"endpoint":{"type":"string","title":"Endpoint","description":"API endpoint that was tested"},"latency_ms":{"type":"number","title":"Latency Ms","description":"Total request latency in milliseconds"},"status_code":{"type":"integer","title":"Status Code","description":"HTTP response status code"},"video_id":{"type":"string","title":"Video Id","description":"YouTube video ID queried"},"lang":{"type":"string","title":"Lang","description":"Language code requested"},"success":{"type":"boolean","title":"Success","description":"Whether the request was successful"}},"type":"object","required":["endpoint","latency_ms","status_code","video_id","lang","success"],"title":"YouTubeLatencyResponse","description":"Response model for YouTube Transcriptor API latency test results."},"YouTubeOAuthConnectRequest":{"properties":{"youtube_channel_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Channel Handle","description":"YouTube channel handle (e.g. @handle)"},"youtube_channel_avatar":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Channel Avatar","description":"YouTube channel avatar URL"},"channel_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Channel Id","description":"YouTube channel ID"},"channel_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Channel Title","description":"YouTube channel title"},"refresh_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token","description":"Google OAuth2 refresh token"},"is_ypp":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Ypp","description":"Whether the channel is YouTube Partner Program"}},"type":"object","title":"YouTubeOAuthConnectRequest","description":"YouTube channel metadata and refresh token from OAuth callback."},"YouTubeOAuthConnectResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"message":{"type":"string","title":"Message"},"is_ypp":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Ypp"}},"type":"object","required":["success","message"],"title":"YouTubeOAuthConnectResponse","description":"Response confirming YouTube connection."},"YouTubeTotalAggregation":{"properties":{"total_views":{"type":"integer","title":"Total Views","description":"Sum of all views"},"total_likes":{"type":"integer","title":"Total Likes","description":"Sum of all likes"},"total_shares":{"type":"integer","title":"Total Shares","description":"Sum of all shares"},"total_watch_time_minutes":{"type":"integer","title":"Total Watch Time Minutes","description":"Sum of all minutes watched"},"total_watch_time_hours":{"type":"number","title":"Total Watch Time Hours","description":"Sum of all watch time in hours"},"total_videos_published":{"type":"integer","title":"Total Videos Published","description":"Sum of all videos published"},"total_usd_earnings":{"type":"string","title":"Total Usd Earnings","description":"Sum of all USD earnings"},"total_alpha_earnings":{"type":"string","title":"Total Alpha Earnings","description":"Sum of all Alpha earnings"},"unique_days":{"type":"integer","title":"Unique Days","description":"Number of days with data"}},"type":"object","required":["total_views","total_likes","total_shares","total_watch_time_minutes","total_watch_time_hours","total_videos_published","total_usd_earnings","total_alpha_earnings","unique_days"],"title":"YouTubeTotalAggregation","description":"Aggregated totals for YouTube daily summary data."},"YtAccountDistributionItem":{"properties":{"yt_account_id":{"type":"integer","title":"Yt Account Id","description":"YouTube account ID"},"bitcast_account_id":{"type":"string","title":"Bitcast Account Id","description":"Bitcast account identifier"},"uid":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Uid","description":"Chain UID from miner table (subnet 93)"},"total_usd":{"type":"number","title":"Total Usd","description":"Sum of usd_target from latest validator run"},"total_weight":{"type":"number","title":"Total Weight","description":"Sum of weight from latest validator run","default":0.0},"total_alpha":{"type":"number","title":"Total Alpha","description":"Sum of alpha_target from latest validator run","default":0.0},"video_count":{"type":"integer","title":"Video Count","description":"Number of videos matched to briefs"}},"type":"object","required":["yt_account_id","bitcast_account_id","total_usd","video_count"],"title":"YtAccountDistributionItem","description":"Individual YouTube account distribution from latest validator run."},"YtAccountEarningsItem":{"properties":{"yt_account_id":{"type":"integer","title":"Yt Account Id","description":"YouTube account ID"},"bitcast_account_id":{"type":"string","title":"Bitcast Account Id","description":"Bitcast account identifier"},"account_title":{"type":"string","title":"Account Title","description":"YouTube channel title"},"portal_user_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Portal User Id","description":"Linked portal user ID (null if unlinked)"},"portal_user_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Portal User Email","description":"Linked portal user email (null if unlinked)"},"is_linked":{"type":"boolean","title":"Is Linked","description":"Whether account is linked to a portal user"},"usd_total":{"type":"number","title":"Usd Total","description":"Total USD earnings (weight-corrected)"},"alpha_total":{"type":"number","title":"Alpha Total","description":"Total Alpha earnings (weight-corrected)"}},"type":"object","required":["yt_account_id","bitcast_account_id","account_title","is_linked","usd_total","alpha_total"],"title":"YtAccountEarningsItem","description":"Schema for individual YouTube account earnings."},"YtAllAccountsEarningsResponse":{"properties":{"accounts":{"items":{"$ref":"#/components/schemas/YtAccountEarningsItem"},"type":"array","title":"Accounts","description":"List of all YouTube accounts with earnings"},"totals":{"$ref":"#/components/schemas/YtEarningsTotals","description":"Aggregated earnings totals"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of accounts with earnings"}},"type":"object","required":["accounts","totals","total_count"],"title":"YtAllAccountsEarningsResponse","description":"Response schema for all YouTube accounts earnings endpoint."},"YtCredentialInfo":{"properties":{"youtube_account_name":{"type":"string","title":"Youtube Account Name","description":"YouTube account name"},"is_ypp":{"type":"boolean","title":"Is Ypp","description":"Whether the account is YPP (YouTube Partner Program)"},"bitcast_account_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bitcast Account Id","description":"Bitcast account identifier"},"youtube_channel_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Channel Id","description":"YouTube channel ID"}},"type":"object","required":["youtube_account_name","is_ypp"],"title":"YtCredentialInfo","description":"Schema for YouTube credentials information."},"YtDistributionResponse":{"properties":{"accounts":{"items":{"$ref":"#/components/schemas/YtAccountDistributionItem"},"type":"array","title":"Accounts","description":"List of YouTube accounts with earnings breakdown"},"totals":{"$ref":"#/components/schemas/YtDistributionTotals","description":"Aggregated totals"},"timeframe":{"type":"string","title":"Timeframe","description":"Timeframe filter applied ('30d' or 'all')"},"total_count":{"type":"integer","title":"Total Count","description":"Total number of accounts"}},"type":"object","required":["accounts","totals","timeframe","total_count"],"title":"YtDistributionResponse","description":"Response schema for YouTube distribution endpoint."},"YtDistributionTotals":{"properties":{"total_usd":{"type":"number","title":"Total Usd","description":"Sum of all usd_target"},"total_accounts":{"type":"integer","title":"Total Accounts","description":"Total number of accounts with earnings"},"total_videos":{"type":"integer","title":"Total Videos","description":"Total videos matched to briefs","default":0},"total_weight":{"type":"number","title":"Total Weight","description":"Sum of all weight","default":0.0},"total_alpha":{"type":"number","title":"Total Alpha","description":"Sum of all alpha","default":0.0}},"type":"object","required":["total_usd","total_accounts"],"title":"YtDistributionTotals","description":"Aggregated totals for YouTube distribution."},"YtEarningsTotals":{"properties":{"total_usd":{"type":"number","title":"Total Usd","description":"Sum of all USD earnings"},"total_alpha":{"type":"number","title":"Total Alpha","description":"Sum of all Alpha earnings"},"linked_accounts_count":{"type":"integer","title":"Linked Accounts Count","description":"Number of accounts linked to portal users"},"unlinked_accounts_count":{"type":"integer","title":"Unlinked Accounts Count","description":"Number of accounts not linked to portal users"},"linked_usd":{"type":"number","title":"Linked Usd","description":"USD earnings from linked accounts"},"unlinked_usd":{"type":"number","title":"Unlinked Usd","description":"USD earnings from unlinked accounts"}},"type":"object","required":["total_usd","total_alpha","linked_accounts_count","unlinked_accounts_count","linked_usd","unlinked_usd"],"title":"YtEarningsTotals","description":"Aggregated totals for YouTube earnings."},"app__schemas__brief__BriefDailyViewsResponse":{"properties":{"brief_id":{"type":"string","title":"Brief Id","description":"Brief ID string"},"creators":{"items":{"type":"string"},"type":"array","title":"Creators","description":"List of creator (channel) names"},"daily_data":{"items":{"type":"object"},"type":"array","title":"Daily Data","description":"Daily view data. Each entry has 'date' plus a key per creator with view count."}},"type":"object","required":["brief_id","creators","daily_data"],"title":"BriefDailyViewsResponse","description":"Response schema for daily views by creator endpoint."},"app__schemas__twitter_brief__BriefDailyViewsResponse":{"properties":{"brief_id":{"type":"string","title":"Brief Id","description":"Brief identifier"},"days":{"items":{"$ref":"#/components/schemas/DailyViewsDay"},"type":"array","title":"Days","description":"Daily per-tweet views, ordered by date ASC"}},"type":"object","required":["brief_id","days"],"title":"BriefDailyViewsResponse","description":"Response for GET /api/v2/twitter/briefs/{brief_id}/daily-views"}},"securitySchemes":{"APIKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key"},"HTTPBearer":{"type":"http","scheme":"bearer"}}}}