{"components":{"responses":{},"schemas":{"CreateAuthSessionRequest":{"properties":{"client_ip":{"description":"Optional end-user IP address used only for the session's coarse geo-location (city/country); the raw IP is never returned. Intended for trusted server-side integrators whose connecting IP is a datacenter egress (e.g. a Next.js app on Vercel): forward the real viewer IP here so the stored location reflects the viewer, not your server. When omitted, the server uses the proxy-aware connecting IP. Accepts IPv4 or IPv6; an unparseable value is ignored and the connecting IP is used.","nullable":true,"type":"string"},"device_id":{"type":"string"},"device_label":{"description":"Optional human-readable device label. When omitted (the usual case), the server derives one from `user_agent` (e.g. \"Chrome · macOS\"). A non-blank value supplied here takes precedence and is stored as-is (max 255 chars).","nullable":true,"type":"string"},"platform":{"enum":["web","ios","android"],"type":"string"},"user_agent":{"nullable":true,"type":"string"}},"required":["device_id","platform"],"title":"CreateAuthSessionRequest","type":"object"},"AiRecommendationsRequest":{"description":"Request for AI-powered video recommendations","example":{"limit":5,"topics":["authentication","user management"]},"properties":{"collection_id":{"description":"Filter to a specific collection (legacy single-value filter)","nullable":true,"type":"string"},"collection_ids":{"description":"Filter to multiple collections (OR semantics)","items":{"type":"string"},"nullable":true,"type":"array"},"include_guidance":{"default":true,"description":"Include AI guidance text explaining recommendations","type":"boolean"},"limit":{"description":"Maximum recommendations (default: 5, max: 20)","example":5,"type":"integer"},"stream":{"default":true,"description":"Stream response via SSE (default: true)","type":"boolean"},"tags":{"description":"Filter by video tags","items":{"type":"string"},"nullable":true,"type":"array"},"topics":{"description":"Topics or learning goals","example":["authentication","getting started"],"items":{"type":"string"},"type":"array"}},"required":["topics"],"title":"AiRecommendationsRequest","type":"object"},"VideoIngestRequest":{"properties":{"callback_url":{"description":"HTTPS URL to receive a webhook POST when video processing completes or fails. Must use HTTPS.","nullable":true,"type":"string"},"collection_id":{"description":"Collection to add the video to","format":"uuid","type":"string"},"description":{"description":"Video description","type":"string"},"external_id":{"description":"Caller's reference ID. Used for idempotency within tenant.","type":"string"},"language":{"description":"ISO 639-1 language code (default: \"en\")","type":"string"},"source_url":{"description":"URL to download the video from","type":"string"},"tags":{"description":"Tag names to assign (created if they don't exist)","items":{"type":"string"},"type":"array"},"title":{"description":"Video title","type":"string"}},"required":["source_url","title"],"title":"VideoIngestRequest","type":"object"},"ConversationSummary":{"properties":{"conversation_id":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"message_count":{"type":"integer"},"preview":{"nullable":true,"type":"string"},"scope":{"description":"Conversation scope: video (video-scoped chat) or library (library-wide chat)","enum":["video","library"],"nullable":true,"type":"string"},"status":{"type":"string"},"title":{"description":"Conversation title (auto-generated)","nullable":true,"type":"string"},"updated_at":{"format":"date-time","type":"string"}},"required":["conversation_id","status","message_count","created_at","updated_at"],"title":"ConversationSummary","type":"object"},"Viewer":{"description":"An external viewer (user from course platform)","properties":{"avatar_url":{"description":"URL to avatar image","example":"https://example.com/avatars/john.jpg","format":"uri","nullable":true,"type":"string"},"computed_display_name":{"description":"Computed display name (priority: display_name > first+last name > first name > name > 'Anonymous')","example":"John Doe","type":"string"},"created_at":{"description":"Creation timestamp","format":"date-time","type":"string"},"display_name":{"description":"Preferred display name","example":"Johnny D","nullable":true,"type":"string"},"email":{"description":"Viewer email","example":"john@example.com","format":"email","type":"string"},"external_id":{"description":"External ID from course platform","example":"usr_123abc","nullable":true,"type":"string"},"first_name":{"description":"First name","example":"John","nullable":true,"type":"string"},"id":{"description":"Viewer UUID","format":"uuid","type":"string"},"is_admin":{"description":"Whether this viewer is a community administrator (read-only via API)","example":false,"type":"boolean"},"last_name":{"description":"Last name","example":"Doe","nullable":true,"type":"string"},"name":{"description":"Viewer name","example":"John Doe","type":"string"},"traits":{"description":"Custom traits map","example":{"business_name":"Acme Corp","stage":"growth"},"nullable":true,"type":"object"},"updated_at":{"description":"Last update timestamp","format":"date-time","type":"string"}},"required":["id","name","computed_display_name"],"title":"Viewer","type":"object"},"ReactionResponse":{"description":"Response after toggling a reaction on a post or comment","example":{"data":{"reacted":true,"reactions_count":5}},"properties":{"data":{"description":"Reaction result","properties":{"reacted":{"description":"Whether the viewer currently has an active reaction","example":true,"type":"boolean"},"reactions_count":{"description":"Updated total number of reactions","example":5,"type":"integer"}},"required":["reacted","reactions_count"],"type":"object"}},"required":["data"],"title":"ReactionResponse","type":"object"},"VideoIngestResponse":{"properties":{"callback_status":{"description":"Callback delivery status","enum":["pending","delivered","failed","blocked"],"nullable":true,"type":"string"},"callback_url":{"description":"Webhook URL for processing callbacks","nullable":true,"type":"string"},"created_at":{"description":"Creation timestamp","format":"date-time","type":"string"},"external_id":{"description":"Caller's reference ID","nullable":true,"type":"string"},"id":{"description":"Video UUID","format":"uuid","type":"string"},"status":{"description":"Current processing status","type":"string"}},"required":["id","status","created_at"],"title":"VideoIngestResponse","type":"object"},"SessionManagementSessionWithRevocation":{"properties":{"device_id":{"type":"string"},"device_label":{"description":"Server-derived human-readable label for the device/browser and OS (e.g. \"Chrome · macOS\", \"Safari · iPhone\"). Derived from the `user_agent` supplied at session creation. Nullable when the user_agent is absent or unrecognised; callers should fall back to `device_id` for display.","nullable":true,"type":"string"},"id":{"format":"uuid","type":"string"},"inserted_at":{"format":"date-time","type":"string"},"last_seen_at":{"format":"date-time","type":"string"},"location":{"description":"Coarse, pre-formatted client location (e.g. \"Berlin, DE\" or \"DE\"). Never the raw IP.","nullable":true,"type":"string"},"platform":{"type":"string"},"revoked_at":{"format":"date-time","nullable":true,"type":"string"},"revoked_by":{"nullable":true,"type":"string"},"revoked_reason":{"nullable":true,"type":"string"},"travel_verdict":{"description":"Impossible-travel verdict label. \"impossible_travel\" when the sign-in was flagged as physically implausible, otherwise null. Label only — never exposes coordinates, the raw IP, or derived distance/speed metrics.","nullable":true,"type":"string"}},"title":"SessionManagementSessionWithRevocation","type":"object"},"AuthChallengeResendResponse":{"properties":{"challenge_id":{"format":"uuid","type":"string"},"email_hint":{"nullable":true,"type":"string"},"expires_at":{"format":"date-time","type":"string"},"ok":{"type":"boolean"},"resend_count":{"type":"integer"}},"required":["ok","challenge_id","email_hint","resend_count","expires_at"],"title":"AuthChallengeResendResponse","type":"object"},"AdminAuthPolicyUpdateRequest":{"properties":{"admin_override_per_user":{"type":"boolean"},"allow_member_self_manage":{"type":"boolean"},"auth_challenge_product_name":{"nullable":true,"type":"string"},"auth_challenge_reply_to_email":{"format":"email","nullable":true,"type":"string"},"auth_challenge_sender_name":{"nullable":true,"type":"string"},"auth_challenge_support_url":{"format":"uri","nullable":true,"type":"string"},"auth_challenge_template_id":{"nullable":true,"type":"string"},"enforcement_mode":{"enum":["block_new","kick_oldest","challenge_new"],"type":"string"},"enforcement_phase":{"enum":["off","shadow","enforce"],"type":"string"},"impossible_travel_mode":{"description":"Impossible travel detection mode. off disables detection.","enum":["off","flag_only","challenge"],"type":"string"},"impossible_travel_speed_kmh":{"description":"Speed threshold in km/h above which a login is flagged as impossible travel.","minimum":1,"type":"integer"},"max_devices_per_user":{"minimum":1,"type":"integer"},"session_ttl_days":{"minimum":1,"type":"integer"}},"title":"AdminAuthPolicyUpdateRequest","type":"object"},"VerifyAuthSessionResponse":{"properties":{"reason":{"enum":["session_not_found","session_revoked","session_expired"],"nullable":true,"type":"string"},"valid":{"type":"boolean"}},"required":["valid"],"title":"VerifyAuthSessionResponse","type":"object"},"CommunityPostResponse":{"description":"Response containing a single community post with comments","properties":{"data":{"$ref":"#/components/schemas/CommunityPost"}},"required":["data"],"title":"CommunityPostResponse","type":"object"},"AuthSessionResponse":{"properties":{"bypassed":{"nullable":true,"type":"boolean"},"expires_at":{"format":"date-time","nullable":true,"type":"string"},"session_id":{"format":"uuid","nullable":true,"type":"string"}},"title":"AuthSessionResponse","type":"object"},"UnifiedConversationSummary":{"description":"Conversation summary with scope indicator and optional video context","properties":{"conversation_id":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"message_count":{"type":"integer"},"preview":{"description":"Preview of first user message","nullable":true,"type":"string"},"scope":{"description":"Conversation scope","enum":["video","library"],"type":"string"},"status":{"type":"string"},"title":{"description":"Conversation title (auto-generated)","nullable":true,"type":"string"},"updated_at":{"format":"date-time","type":"string"},"video":{"anyOf":[{"$ref":"#/components/schemas/ConversationVideoContext"}],"description":"Video context for video-scoped conversations, null for library conversations","nullable":true}},"required":["conversation_id","scope","status","message_count","created_at","updated_at"],"title":"UnifiedConversationSummary","type":"object"},"AdminDeviceLimitResponse":{"properties":{"data":{"properties":{"device_limit_override":{"nullable":true,"type":"integer"},"viewer_id":{"format":"uuid","type":"string"}},"type":"object"}},"required":["data"],"title":"AdminDeviceLimitResponse","type":"object"},"LibraryConversationListResponse":{"properties":{"conversations":{"items":{"$ref":"#/components/schemas/ConversationSummary"},"type":"array"},"pagination":{"$ref":"#/components/schemas/ConversationPagination"}},"required":["conversations","pagination"],"title":"LibraryConversationListResponse","type":"object"},"CommunityPostCreate":{"description":"Parameters for creating a community post","example":{"post":{"category":"announcements","content":"Welcome to our community!"}},"properties":{"post":{"description":"Post parameters","properties":{"category":{"description":"Post category (optional, defaults to 'general')","enum":["announcements","q_and_a","general"],"example":"announcements","type":"string"},"content":{"description":"Post content (required)","example":"Welcome to our community!","type":"string"}},"required":["content"],"type":"object"}},"required":["post"],"title":"CommunityPostCreate","type":"object"},"SessionManagementDeviceLimitRequest":{"properties":{"limit":{"description":"Set a positive integer override, or null to clear it.","minimum":1,"nullable":true,"type":"integer"}},"required":["limit"],"title":"SessionManagementDeviceLimitRequest","type":"object"},"AdminAuthPolicyResponse":{"properties":{"admin_override_per_user":{"type":"boolean"},"allow_member_self_manage":{"type":"boolean"},"auth_challenge_product_name":{"nullable":true,"type":"string"},"auth_challenge_reply_to_email":{"format":"email","nullable":true,"type":"string"},"auth_challenge_sender_name":{"nullable":true,"type":"string"},"auth_challenge_support_url":{"format":"uri","nullable":true,"type":"string"},"auth_challenge_template_id":{"nullable":true,"type":"string"},"enforcement_mode":{"enum":["block_new","kick_oldest","challenge_new"],"type":"string"},"enforcement_phase":{"enum":["off","shadow","enforce"],"type":"string"},"impossible_travel_mode":{"description":"Impossible travel detection mode. off disables detection.","enum":["off","flag_only","challenge"],"type":"string"},"impossible_travel_speed_kmh":{"description":"Speed threshold in km/h above which a login is flagged as impossible travel.","minimum":1,"type":"integer"},"inserted_at":{"format":"date-time","type":"string"},"max_devices_per_user":{"type":"integer"},"session_ttl_days":{"type":"integer"},"updated_at":{"format":"date-time","type":"string"}},"title":"AdminAuthPolicyResponse","type":"object"},"SessionManagementViewerStateResponse":{"properties":{"data":{"$ref":"#/components/schemas/SessionManagementViewerState"}},"required":["data"],"title":"SessionManagementViewerStateResponse","type":"object"},"SessionManagementViewerState":{"properties":{"active_session_count":{"minimum":0,"type":"integer"},"admin_overrides_allowed":{"type":"boolean"},"can_update_device_limit_override":{"description":"Whether this tenant key can set or clear the viewer's numeric override.","type":"boolean"},"can_update_session_management_exemption":{"description":"Whether this tenant key can set or clear the viewer's exemption flag.","type":"boolean"},"device_limit_override":{"minimum":1,"nullable":true,"type":"integer"},"effective_device_limit":{"minimum":1,"nullable":true,"type":"integer"},"effective_device_limit_source":{"enum":["policy","override","exempt"],"type":"string"},"external_id":{"type":"string"},"policy_default_device_limit":{"minimum":1,"type":"integer"},"session_management_exempt":{"type":"boolean"},"viewer_id":{"format":"uuid","type":"string"}},"required":["viewer_id","external_id","active_session_count","policy_default_device_limit","effective_device_limit","effective_device_limit_source","device_limit_override","session_management_exempt","admin_overrides_allowed","can_update_device_limit_override","can_update_session_management_exemption"],"title":"SessionManagementViewerState","type":"object"},"CommunityPost":{"description":"A community post","properties":{"author":{"$ref":"#/components/schemas/CommunityAuthor"},"category":{"description":"Post category","enum":["announcements","q_and_a","general"],"example":"announcements","type":"string"},"comments":{"description":"Comments summary","properties":{"commented_by":{"description":"Users who commented (unique, capped at 10)","items":{"$ref":"#/components/schemas/CommunityAuthor"},"maxItems":10,"type":"array"},"count":{"description":"Total number of comments","example":3,"type":"integer"},"items":{"description":"Full comment threads (only in detail endpoint)","items":{"$ref":"#/components/schemas/CommunityComment"},"type":"array"}},"required":["count","commented_by"],"type":"object"},"content":{"description":"Post content","example":"Welcome to our community!","type":"string"},"created_at":{"description":"Creation timestamp","format":"date-time","type":"string"},"id":{"description":"Post UUID","format":"uuid","type":"string"},"pinned":{"description":"Whether the post is pinned","example":false,"type":"boolean"},"pinned_at":{"description":"When the post was pinned","format":"date-time","nullable":true,"type":"string"},"reactions":{"description":"Reactions summary","properties":{"count":{"description":"Total number of reactions","example":10,"type":"integer"},"reacted_by":{"description":"Users who reacted (most recent, capped at 10)","items":{"$ref":"#/components/schemas/CommunityAuthor"},"maxItems":10,"type":"array"},"viewer_has_reacted":{"description":"Whether the current viewer has reacted (only when X-Viewer-ID header is provided)","nullable":true,"type":"boolean"}},"required":["count","reacted_by"],"type":"object"},"updated_at":{"description":"Last update timestamp","format":"date-time","type":"string"}},"required":["id","content","category","pinned","author","reactions","comments","created_at"],"title":"CommunityPost","type":"object"},"SessionManagementExemptionRequest":{"properties":{"exempt":{"description":"Whether this viewer is exempt from session-management enforcement.","type":"boolean"}},"required":["exempt"],"title":"SessionManagementExemptionRequest","type":"object"},"ViewerResponse":{"description":"Response containing a single viewer","properties":{"data":{"$ref":"#/components/schemas/Viewer"}},"required":["data"],"title":"ViewerResponse","type":"object"},"AuthChallengeError":{"properties":{"attempts_remaining":{"nullable":true,"type":"integer"},"error":{"enum":["bad_request","challenge_invalid","challenge_expired","code_incorrect","too_many_attempts","resend_rate_limited","resend_limit_exceeded","viewer_email_required","challenge_delivery_unavailable","viewer_not_found","forbidden"],"type":"string"},"retry_after":{"nullable":true,"type":"integer"}},"required":["error"],"title":"AuthChallengeError","type":"object"},"AuthSessionsListResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ActiveSessionSummary"},"type":"array"}},"required":["data"],"title":"AuthSessionsListResponse","type":"object"},"VideoResponse":{"description":"Response containing a single video","properties":{"data":{"$ref":"#/components/schemas/Video"}},"required":["data"],"title":"VideoResponse","type":"object"},"VideoProgress":{"description":"A viewer's watch progress on a video","example":{"completed":false,"completed_at":null,"current_time":300,"duration":1000,"id":"550e8400-e29b-41d4-a716-446655440000","percentage":30.0},"nullable":true,"properties":{"completed":{"description":"Whether the viewer has completed watching","example":false,"type":"boolean"},"completed_at":{"description":"Timestamp when video was marked complete","format":"date-time","nullable":true,"type":"string"},"current_time":{"description":"Current playback position in seconds","example":300,"type":"integer"},"duration":{"description":"Total video duration in seconds","example":1000,"type":"integer"},"id":{"description":"Progress record ID","format":"uuid","type":"string"},"percentage":{"description":"Watch progress percentage (0-100)","example":30.0,"format":"float","type":"number"}},"title":"VideoProgress","type":"object"},"Citation":{"description":"A source reference from video content.\n\nThe `cited` field indicates whether this source is referenced in the answer text.\nCitation indices in the answer (e.g., `[4]`) map to array positions: `[4]` → `sources[3]`.\n\n**Field naming**: Use the canonical field names (`title`, `timestamp`, `timestamp_end`, `playback_id`).\nDeprecated aliases (`video_title`, `timestamp_seconds`, `timestamp_end_seconds`, `mux_playback_id`)\nare included for backward compatibility and will be removed in a future release.\n","properties":{"canonical_url":{"description":"Canonical video URL for click-through","nullable":true,"type":"string"},"cited":{"description":"Whether this source is cited in the answer text. Filter by `cited: true` to show only referenced sources.","type":"boolean"},"document_type":{"description":"Document type (transcript or attachment)","type":"string"},"id":{"description":"Citation ID","type":"string"},"kind":{"description":"Discriminates citation provenance:\n- `\"chunk\"` (default) — transcript chunk, resource, or attachment\n- `\"moment\"` — LLM-extracted curated moment (insight / story / framework / q_and_a / exercise)\nWhen `\"moment\"`, the `moment_id`, `type`, `summary`, and `transcript_excerpt`\nfields are populated.\n","enum":["chunk","moment"],"type":"string"},"matched_sentence":{"description":"The transcript sentence whose text best matches the cited claim, when a\nconfident match was found. When present, the citation's `timestamp` /\n`timestamp_end` are narrowed to this sentence instead of the coarse chunk\nbounds. The key is **omitted entirely** (not null) for legacy chunks or\nlow-confidence matches, so the citation falls back to chunk-level timing.\n","nullable":true,"properties":{"end_ms":{"description":"Sentence end (ms)","type":"integer"},"speaker_label":{"description":"Raw diarization label for this sentence, or null.","nullable":true,"type":"string"},"speaker_name":{"description":"Resolved speaker name for this sentence, or null.","nullable":true,"type":"string"},"start_ms":{"description":"Sentence start (ms)","type":"integer"},"text":{"description":"Sentence text","type":"string"},"timestamp":{"description":"Sentence start in seconds (canonical)","type":"integer"},"timestamp_end":{"description":"Sentence end in seconds (canonical)","type":"integer"},"timestamp_end_seconds":{"deprecated":true,"description":"DEPRECATED: Use `timestamp_end` instead","type":"integer"},"timestamp_seconds":{"deprecated":true,"description":"DEPRECATED: Use `timestamp` instead","type":"integer"}},"type":"object"},"moment_id":{"description":"Source moment UUID (present only when `kind=\"moment\"`)","nullable":true,"type":"string"},"mux_playback_id":{"deprecated":true,"description":"DEPRECATED: Use `playback_id` instead","type":"string"},"playback_id":{"description":"Mux playback ID","type":"string"},"published_at":{"description":"Publication timestamp (Unix epoch seconds)","nullable":true,"type":"integer"},"slug":{"description":"Video slug when available","nullable":true,"type":"string"},"speaker":{"description":"Resolved speaker name when identified, otherwise the raw diarization label (back-compat). Prefer `speaker_name`.","nullable":true,"type":"string"},"speaker_label":{"description":"Raw diarization label (e.g. \"A\"), or null.","nullable":true,"type":"string"},"speaker_name":{"description":"Resolved speaker display name, or null when the speaker is unnamed or the chunk predates speaker resolution.","nullable":true,"type":"string"},"summary":{"description":"Curated LLM-extracted summary of the moment (present only when `kind=\"moment\"`). Frontend may show this as the primary label for the citation pill.","nullable":true,"type":"string"},"text":{"description":"Quoted text","type":"string"},"timestamp":{"description":"Start time in seconds","type":"integer"},"timestamp_end":{"description":"End time in seconds","type":"integer"},"timestamp_end_seconds":{"deprecated":true,"description":"DEPRECATED: Use `timestamp_end` instead","type":"integer"},"timestamp_seconds":{"deprecated":true,"description":"DEPRECATED: Use `timestamp` instead","type":"integer"},"timestamp_url":{"description":"Canonical URL with `?t=<seconds>` when timestamp is available","nullable":true,"type":"string"},"title":{"description":"Video title","type":"string"},"transcript_excerpt":{"description":"Verbatim transcript text covered by the moment (present only when `kind=\"moment\"`). Mirrors `text` for clients that don't discriminate by `kind`.","nullable":true,"type":"string"},"type":{"description":"Moment type (present only when `kind=\"moment\"`)","enum":["insight","story","framework","q_and_a","exercise"],"nullable":true,"type":"string"},"video_id":{"description":"Source video ID","type":"string"},"video_title":{"deprecated":true,"description":"DEPRECATED: Use `title` instead","type":"string"}},"required":["id","video_id","title","text","timestamp","cited"],"title":"Citation","type":"object"},"ActiveSessionSummary":{"properties":{"device_id":{"type":"string"},"device_label":{"description":"Server-derived human-readable label for the device/browser and OS (e.g. \"Chrome · macOS\", \"Safari · iPhone\"). Derived from the `user_agent` supplied at session creation. Nullable when the user_agent is absent or unrecognised; callers should fall back to `device_id` for display.","nullable":true,"type":"string"},"id":{"format":"uuid","type":"string"},"inserted_at":{"format":"date-time","type":"string"},"last_seen_at":{"format":"date-time","type":"string"},"platform":{"type":"string"}},"title":"ActiveSessionSummary","type":"object"},"RevokeOthersResponse":{"properties":{"ok":{"type":"boolean"},"revoked_count":{"type":"integer"}},"required":["ok","revoked_count"],"title":"RevokeOthersResponse","type":"object"},"MultimodalSettings":{"description":"Per-account capability flag for multimodal (image input) on AI endpoints.\n\nWhen `enabled` is `true`, clients may attach the `images` array on requests to\n`/api/v1/ai/chat`, `/api/v1/ai/search`, and `/api/v1/ai/videos/:id/chat`\n(`/api/v1/ai/ask` remains as a deprecated alias for `/api/v1/ai/chat`). When\n`false`, those requests are rejected with `feature_disabled`.\n\nLimits (`max_images`, `accepted_media_types`) are only included when enabled.\n","properties":{"accepted_media_types":{"description":"Accepted base64 media types (only present when enabled)","example":["image/png","image/jpeg","image/webp","image/gif"],"items":{"type":"string"},"type":"array"},"enabled":{"description":"Whether image inputs are accepted on AI endpoints","example":false,"type":"boolean"},"max_images":{"description":"Maximum number of images per request (only present when enabled)","example":5,"type":"integer"}},"required":["enabled"],"title":"MultimodalSettings","type":"object"},"Collection":{"description":"A video collection that can be nested in a hierarchy","properties":{"children":{"description":"Child collections (only present when include=children or include=descendants)","items":{"description":"Nested Collection object","type":"object"},"nullable":true,"type":"array"},"children_count":{"description":"Number of direct child collections","example":3,"type":"integer"},"created_at":{"description":"Creation timestamp","format":"date-time","type":"string"},"id":{"description":"Collection UUID","format":"uuid","type":"string"},"name":{"description":"Collection name","example":"Getting Started","type":"string"},"parent_id":{"description":"Parent collection UUID (null for root collections)","format":"uuid","nullable":true,"type":"string"},"position":{"description":"Position within parent (0-indexed)","example":0,"type":"integer"},"slug":{"description":"URL-friendly slug","example":"getting-started","type":"string"},"updated_at":{"description":"Last update timestamp","format":"date-time","type":"string"},"video_count":{"description":"Number of public videos in collection","example":5,"type":"integer"}},"required":["id","name","slug"],"title":"Collection","type":"object"},"AdminRevokeAuthSessionRequest":{"properties":{"reason":{"nullable":true,"type":"string"}},"title":"AdminRevokeAuthSessionRequest","type":"object"},"AdminRevokeAllResponse":{"properties":{"ok":{"type":"boolean"},"revoked_count":{"type":"integer"}},"required":["ok","revoked_count"],"title":"AdminRevokeAllResponse","type":"object"},"SearchResponse":{"description":"Search results for public videos and transcript content.","example":{"hits":[{"document_type":"transcript","duration_ms":5000,"id":"550e8400-e29b-41d4-a716-446655440000_1_5000-10000","speaker":"Speaker 1","text":"The first step is to connect your account...","timestamp_end_ms":10000,"timestamp_start_ms":5000,"title":"Getting Started","video_id":"550e8400-e29b-41d4-a716-446655440000"}],"page":1,"per_page":10,"processing_time_ms":4,"total_hits":1},"properties":{"hits":{"description":"Matching search hits","items":{"$ref":"#/components/schemas/SearchHit"},"type":"array"},"page":{"description":"Current page number","type":"integer"},"per_page":{"description":"Results per page","type":"integer"},"processing_time_ms":{"description":"Search processing time in milliseconds","type":"integer"},"total_hits":{"description":"Estimated total matching hits","type":"integer"}},"required":["hits","page","per_page","total_hits","processing_time_ms"],"title":"SearchResponse","type":"object"},"LibraryAiChatRequest":{"description":"Chat request across your entire video library","example":{"prompt":"What are the best practices for user authentication?","stream":false},"properties":{"collection_id":{"description":"Limit search to a specific collection (legacy single-value filter)","nullable":true,"type":"string"},"collection_ids":{"description":"Limit search to multiple collections (OR semantics)","items":{"type":"string"},"nullable":true,"type":"array"},"conversation_id":{"description":"Continue an existing conversation","nullable":true,"type":"string"},"images":{"description":"Optional array of images (max 5). Requires the Vision feature flag. Each image can be base64-encoded or a URL.","items":{"anyOf":[{"$ref":"#/components/schemas/AiImageBase64"},{"$ref":"#/components/schemas/AiImageUrl"}]},"maxItems":5,"nullable":true,"type":"array"},"prompt":{"description":"Your question","example":"What videos cover authentication?","type":"string"},"stream":{"default":true,"description":"Stream response via SSE (default: true)","type":"boolean"},"tags":{"description":"Filter by video tags","items":{"type":"string"},"nullable":true,"type":"array"},"viewer":{"description":"Viewer identifier (UUID or your external ID) to associate with the conversation. When the viewers feature is enabled, the viewer's traits also personalize AI responses.","nullable":true,"type":"string"},"viewer_profile":{"anyOf":[{"description":"Free text or markdown describing the viewer; replaces the synced viewer's traits for this request. Up to 8000 characters.","example":"Sarah runs a 3-person agency, pre-launch, wants her first 10 customers.","type":"string"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Key-value traits merged over the synced viewer's traits (inline wins per key). Max 20 keys; keys must match ^[a-zA-Z_][a-zA-Z0-9_]*$; values are strings (up to 2000 characters), numbers, or booleans.","example":{"business_name":"Acme Corp","goal":"find first customers","stage":"growth"},"type":"object"}],"description":"Tell the AI about the person asking, for this request only (never persisted). Pass free text/markdown, or a key-value object. Requires the viewers feature.","nullable":true}},"required":["prompt"],"title":"LibraryAiChatRequest","type":"object"},"DeviceLimitError":{"properties":{"active_sessions":{"items":{"$ref":"#/components/schemas/ActiveSessionSummary"},"type":"array"},"error":{"enum":["device_limit_exceeded"],"type":"string"}},"required":["error"],"title":"DeviceLimitError","type":"object"},"AdminSessionWithRevocation":{"properties":{"device_id":{"type":"string"},"device_label":{"description":"Server-derived human-readable label for the device/browser and OS (e.g. \"Chrome · macOS\", \"Safari · iPhone\"). Derived from the `user_agent` supplied at session creation. Nullable when the user_agent is absent or unrecognised; callers should fall back to `device_id` for display.","nullable":true,"type":"string"},"id":{"format":"uuid","type":"string"},"inserted_at":{"format":"date-time","type":"string"},"last_seen_at":{"format":"date-time","type":"string"},"location":{"description":"Coarse, pre-formatted client location (e.g. \"Berlin, DE\" or \"DE\"). Never the raw IP.","nullable":true,"type":"string"},"platform":{"type":"string"},"revoked_at":{"format":"date-time","nullable":true,"type":"string"},"revoked_by":{"nullable":true,"type":"string"},"revoked_reason":{"nullable":true,"type":"string"},"travel_verdict":{"description":"Impossible-travel verdict label. \"impossible_travel\" when the sign-in was flagged as physically implausible, otherwise null. Label only — never exposes coordinates, the raw IP, or derived distance/speed metrics.","nullable":true,"type":"string"}},"title":"AdminSessionWithRevocation","type":"object"},"CollectionResponse":{"description":"Response containing a single collection","properties":{"data":{"$ref":"#/components/schemas/Collection"}},"required":["data"],"title":"CollectionResponse","type":"object"},"Video":{"description":"A video resource","properties":{"download_urls":{"description":"Available download URLs keyed by quality or asset type","nullable":true,"type":"object"},"title":{"description":"Video title","example":"Introduction to BOLD","type":"string"},"id":{"description":"Video ID (hashid)","example":"abc123","type":"string"},"cta":{"description":"Effective call-to-action for this video","nullable":true,"type":"object"},"chapters_url":{"description":"Signed URL for chapter data","nullable":true,"type":"string"},"playback_id":{"description":"Mux playback ID","nullable":true,"type":"string"},"languages":{"description":"Available languages for this video: the source language plus any ready translations. Always present; a video with no translations returns just its source entry.","items":{"properties":{"code":{"description":"Language code","example":"es","type":"string"},"mux_audio_track_id":{"description":"Mux audio track reference (null for the source language)","nullable":true,"type":"string"},"mux_subtitle_track_id":{"description":"Mux subtitle track reference (null for the source language)","nullable":true,"type":"string"},"name":{"description":"Display name","example":"Spanish","type":"string"},"ready":{"description":"Whether this language is playable","type":"boolean"},"role":{"description":"Whether this is the original language or a translation","enum":["source","translation"],"type":"string"},"subtitle_url":{"description":"Subtitle (VTT) URL for this language","nullable":true,"type":"string"}},"type":"object"},"type":"array"},"internal_id":{"description":"Internal UUID","example":"550e8400-e29b-41d4-a716-446655440000","format":"uuid","type":"string"},"type":{"description":"Resource type","example":"video","type":"string"},"stream_url":{"description":"Playable stream URL","nullable":true,"type":"string"},"source_id":{"description":"Original provider's video ID (mirrors imported_id). Included only when playback_mode is \"source\".","example":"dQw4w9WgXcQ","nullable":true,"type":"string"},"legacy_video_url":{"description":"Legacy source video URL when available","nullable":true,"type":"string"},"duration":{"description":"Duration in seconds","example":120,"type":"integer"},"source_provider":{"description":"Original provider key (mirrors imported_from). Included only when playback_mode is \"source\".","example":"youtube","nullable":true,"type":"string"},"updated_at":{"description":"Last update timestamp","format":"date-time","type":"string"},"created_at":{"description":"Creation timestamp","format":"date-time","type":"string"},"subtitles":{"description":"Subtitle track metadata","nullable":true,"type":"object"},"playback_speed":{"description":"Default playback speed","nullable":true,"type":"number"},"captions_label":{"description":"Caption language label","nullable":true,"type":"string"},"attachments":{"description":"Files attached to this video. Populated on single-video responses and video list responses when attachments are present.","items":{"$ref":"#/components/schemas/Attachment"},"type":"array"},"tags":{"description":"Tags assigned to this video","items":{"type":"object"},"type":"array"},"teaser":{"description":"Short teaser text","nullable":true,"type":"string"},"published_at":{"description":"Publication timestamp","format":"date-time","nullable":true,"type":"string"},"playback_mode":{"description":"How the public player should source the stream. \"native\" plays from Mux; \"source\" embeds the original imported provider (e.g., YouTube). Always present.","enum":["native","source"],"example":"native","type":"string"},"transcript":{"description":"Transcript track URLs keyed by format","type":"object"},"progress":{"description":"Viewer's watch progress (only present when viewer_id is provided)","nullable":true,"oneOf":[{"$ref":"#/components/schemas/VideoProgress"}]},"description":{"description":"Full video description","nullable":true,"type":"string"},"chapters":{"description":"AI-generated chapter data","items":{"type":"object"},"nullable":true,"type":"array"},"captions_lang":{"description":"Caption language code","nullable":true,"type":"string"},"thumbnail":{"description":"Thumbnail URL","nullable":true,"type":"string"},"captions":{"description":"Caption file URL","nullable":true,"type":"string"},"slug":{"description":"URL-friendly identifier (optional, unique)","example":"introduction-to-bold","nullable":true,"type":"string"},"meta_data":{"description":"Embeddable metadata for title, description, and image","type":"object"},"imported_from":{"description":"Original import source when available","nullable":true,"type":"string"}},"required":["id","title"],"title":"Video","type":"object"},"AdminDeviceLimitRequest":{"properties":{"limit":{"minimum":1,"nullable":true,"type":"integer"}},"title":"AdminDeviceLimitRequest","type":"object"},"AdminErrorResponse":{"properties":{"details":{"additionalProperties":true,"description":"Field-level validation errors when present","nullable":true,"type":"object"},"error":{"description":"Machine-readable admin API error code","type":"string"},"status":{"nullable":true,"type":"integer"},"timestamp":{"format":"date-time","nullable":true,"type":"string"}},"required":["error"],"title":"AdminErrorResponse","type":"object"},"SelfManagementDisabledError":{"properties":{"error":{"enum":["self_management_disabled"],"type":"string"}},"required":["error"],"title":"SelfManagementDisabledError","type":"object"},"Attachment":{"description":"A file attached to a video","properties":{"description":{"description":"Optional attachment description","nullable":true,"type":"string"},"file_size":{"description":"Attachment size in bytes","example":42000,"nullable":true,"type":"integer"},"file_url":{"description":"Public URL for downloading the attachment","example":"https://uploads.eu1.boldvideo.io/uploads/bt_demo/attachments/file.pdf","format":"uri","type":"string"},"id":{"description":"Attachment ID","example":123,"type":"integer"},"mime_type":{"description":"Attachment MIME type","example":"application/pdf","nullable":true,"type":"string"},"title":{"description":"Attachment title or original filename","example":"Strategy worksheet.pdf","type":"string"}},"required":["id","title","file_url"],"title":"Attachment","type":"object"},"Playlist":{"description":"A curated list of videos","properties":{"cover_image_url":{"description":"Cover image URL","nullable":true,"type":"string"},"description":{"description":"Playlist description","nullable":true,"type":"string"},"id":{"description":"Playlist ID","type":"string"},"is_private":{"description":"Whether the playlist is private","type":"boolean"},"title":{"description":"Playlist title","example":"Getting Started","type":"string"},"videos":{"description":"Videos in this playlist","items":{"$ref":"#/components/schemas/Video"},"type":"array"}},"required":["id","title","is_private"],"title":"Playlist","type":"object"},"AiRecommendationsResponse":{"description":"AI-curated video recommendations","properties":{"guidance":{"description":"AI explanation of why these videos were recommended","nullable":true,"type":"string"},"recommendations":{"description":"Recommended videos","items":{"$ref":"#/components/schemas/Video"},"type":"array"},"sources":{"description":"Source citations from video content","items":{"$ref":"#/components/schemas/Citation"},"type":"array"}},"required":["recommendations"],"title":"AiRecommendationsResponse","type":"object"},"CommunityCommentCreate":{"description":"Parameters for creating a comment","example":{"comment":{"content":"Great post! Thanks for sharing."}},"properties":{"comment":{"description":"Comment parameters","properties":{"content":{"description":"Comment content (required)","example":"Great post! Thanks for sharing.","type":"string"},"parent_id":{"description":"Parent comment UUID for replies (optional)","format":"uuid","nullable":true,"type":"string"}},"required":["content"],"type":"object"}},"required":["comment"],"title":"CommunityCommentCreate","type":"object"},"ViewerMemoryResponse":{"description":"Response containing a viewer's memory document.","properties":{"data":{"$ref":"#/components/schemas/ViewerMemory"}},"required":["data"],"title":"ViewerMemoryResponse","type":"object"},"VerifyAuthChallengeRequest":{"properties":{"code":{"description":"Six-digit verification code from the latest challenge email.","pattern":"^\\d{6}$","type":"string"}},"required":["code"],"title":"VerifyAuthChallengeRequest","type":"object"},"ViewersResponse":{"description":"Response containing a paginated list of viewers","properties":{"data":{"description":"List of viewers","items":{"$ref":"#/components/schemas/Viewer"},"type":"array"},"meta":{"description":"Pagination metadata","properties":{"page":{"description":"Current page number (1-indexed)","example":1,"type":"integer"},"page_size":{"description":"Items per page","example":20,"type":"integer"},"total_entries":{"description":"Total number of items across all pages","example":47,"type":"integer"},"total_pages":{"description":"Total number of pages","example":3,"type":"integer"}},"required":["page","page_size","total_entries","total_pages"],"type":"object"}},"required":["data","meta"],"title":"ViewersResponse","type":"object"},"PlaylistsResponse":{"description":"List of playlists","properties":{"data":{"description":"Playlists","items":{"$ref":"#/components/schemas/Playlist"},"type":"array"}},"required":["data"],"title":"PlaylistsResponse","type":"object"},"CollectionVideosResponse":{"description":"Response containing videos in a collection","properties":{"data":{"description":"List of videos in the collection","items":{"$ref":"#/components/schemas/Video"},"type":"array"}},"required":["data"],"title":"CollectionVideosResponse","type":"object"},"SessionManagementRevokeSessionRequest":{"properties":{"reason":{"maxLength":255,"nullable":true,"type":"string"}},"title":"SessionManagementRevokeSessionRequest","type":"object"},"AiImageBase64":{"description":"Base64-encoded image","properties":{"data":{"description":"Base64-encoded image data (max 20MB decoded)","type":"string"},"media_type":{"description":"MIME type of the image","enum":["image/png","image/jpeg","image/webp","image/gif"],"type":"string"},"type":{"description":"Image type","enum":["base64"],"type":"string"}},"required":["type","media_type","data"],"title":"AiImageBase64","type":"object"},"LibraryAiRetrievalMetadata":{"description":"Public-safe retrieval status metadata for library AI answers. Internal stage names, timeouts, query counts, and fallback timing are intentionally omitted.","properties":{"degraded":{"description":"True when retrieval recovered through a degraded fallback path","type":"boolean"},"degraded_reason":{"description":"Coarse public reason for degraded retrieval, when degraded is true","enum":["stage1_exhausted"],"nullable":true,"type":"string"},"retrieval_mode":{"description":"Whether the answer used the normal retrieval path or a degraded fallback path","enum":["normal","degraded"],"type":"string"}},"required":["retrieval_mode","degraded"],"title":"LibraryAiRetrievalMetadata","type":"object"},"ProjectSettings":{"description":"Public configuration for your video portal","properties":{"data":{"description":"Settings data","properties":{"account":{"description":"Per-account capability flags","properties":{"multimodal":{"$ref":"#/components/schemas/MultimodalSettings"}},"type":"object"},"ai_avatar":{"description":"AI assistant avatar URL","nullable":true,"type":"string"},"ai_greeting":{"description":"AI greeting message","nullable":true,"type":"string"},"ai_name":{"description":"AI assistant name","nullable":true,"type":"string"},"chat_disclaimer":{"description":"Disclaimer text displayed below the AI chat input","nullable":true,"type":"string"},"favicon_url":{"description":"Favicon URL","nullable":true,"type":"string"},"has_ai":{"description":"Whether AI features are enabled","type":"boolean"},"logo_url":{"description":"Logo URL","nullable":true,"type":"string"},"name":{"description":"Account name","type":"string"},"portal":{"$ref":"#/components/schemas/PortalSettings"},"slug":{"description":"Account slug","type":"string"},"theme_config":{"$ref":"#/components/schemas/ThemeConfig"}},"type":"object"},"version":{"description":"API version","example":"0.1","type":"string"}},"required":["version","data"],"title":"ProjectSettings","type":"object"},"UnifiedConversationListResponse":{"description":"Paginated list of all conversations for a viewer across all scopes","properties":{"conversations":{"items":{"$ref":"#/components/schemas/UnifiedConversationSummary"},"type":"array"},"pagination":{"$ref":"#/components/schemas/ConversationPagination"}},"required":["conversations","pagination"],"title":"UnifiedConversationListResponse","type":"object"},"AuthSessionChallengeResponse":{"properties":{"challenge_id":{"format":"uuid","type":"string"},"challenge_required":{"type":"boolean"},"email_hint":{"description":"Masked destination email for the verification code.","nullable":true,"type":"string"}},"required":["challenge_required","challenge_id","email_hint"],"title":"AuthSessionChallengeResponse","type":"object"},"AnalyticsSettings":{"description":"Analytics provider configuration","properties":{"provider":{"description":"Analytics provider","enum":["none","plausible","ga4","fathom"],"example":"none","type":"string"},"site_id":{"description":"Analytics site ID","nullable":true,"type":"string"}},"required":["provider"],"title":"AnalyticsSettings","type":"object"},"LibraryAiChatResponse":{"description":"AI response with citations from your video library.\n\nCitation indices in the answer text (e.g., `[4]`) map to source array positions: `[4]` → `sources[3]`.\nEach source has a `cited` boolean indicating whether it's referenced in the answer.\n","properties":{"citations":{"deprecated":true,"description":"Deprecated: Use `sources` instead. Same data as sources.","items":{"$ref":"#/components/schemas/Citation"},"type":"array"},"content":{"description":"AI response","type":"string"},"conversation_id":{"description":"Conversation ID for follow-up questions","type":"string"},"metadata":{"anyOf":[{"$ref":"#/components/schemas/LibraryAiRetrievalMetadata"}],"description":"Public-safe retrieval metadata for answer responses. Clarification responses may omit this field.","nullable":true},"response_type":{"description":"Whether this is a direct answer or clarifying question","enum":["answer","clarification"],"type":"string"},"sources":{"description":"All retrieved sources. Each has `cited: true/false` to indicate if referenced in the answer.","items":{"$ref":"#/components/schemas/Citation"},"type":"array"}},"required":["conversation_id","content","response_type","sources"],"title":"LibraryAiChatResponse","type":"object"},"PortalSettings":{"description":"Portal configuration","properties":{"analytics":{"$ref":"#/components/schemas/AnalyticsSettings"},"auth":{"description":"Authentication configuration","type":"object"},"color_scheme":{"description":"Color scheme","enum":["light","dark","toggle"],"type":"string"},"custom_redirects":{"description":"Branded shortlink redirects","items":{"$ref":"#/components/schemas/CustomRedirect"},"type":"array"},"display":{"description":"Display configuration","type":"object"},"hero":{"$ref":"#/components/schemas/HeroSettings"},"layout":{"description":"Layout configuration","type":"object"},"navigation":{"description":"Navigation configuration","type":"object"},"theme":{"description":"Theme configuration","type":"object"}},"title":"PortalSettings","type":"object"},"VideosResponse":{"description":"Response containing a list of videos","properties":{"data":{"description":"List of videos","items":{"$ref":"#/components/schemas/Video"},"type":"array"}},"required":["data"],"title":"VideosResponse","type":"object"},"ViewerUpdate":{"description":"Parameters for updating a viewer","properties":{"avatar_url":{"description":"URL to avatar image","example":"https://example.com/avatars/john.jpg","format":"uri","nullable":true,"type":"string"},"display_name":{"description":"Preferred display name","example":"Johnny D","nullable":true,"type":"string"},"email":{"description":"Viewer email","example":"john@example.com","format":"email","type":"string"},"external_id":{"description":"External ID from course platform","example":"usr_123abc","nullable":true,"type":"string"},"first_name":{"description":"First name","example":"John","nullable":true,"type":"string"},"last_name":{"description":"Last name","example":"Doe","nullable":true,"type":"string"},"name":{"description":"Viewer name","example":"John Doe","type":"string"},"traits":{"description":"Custom traits map","example":{"business_name":"Acme Corp","stage":"growth"},"nullable":true,"type":"object"}},"title":"ViewerUpdate","type":"object"},"AdminRevokeAuthSessionResponse":{"properties":{"ok":{"type":"boolean"},"session_id":{"format":"uuid","type":"string"}},"required":["ok","session_id"],"title":"AdminRevokeAuthSessionResponse","type":"object"},"ViewerMemoryUpdate":{"description":"Parameters for updating a viewer's memory settings.","properties":{"memory":{"description":"Memory settings to update.","properties":{"enabled":{"description":"Set false to opt the viewer out of re-distillation; true to opt back in.","example":false,"type":"boolean"}},"required":["enabled"],"type":"object"}},"required":["memory"],"title":"ViewerMemoryUpdate","type":"object"},"SearchHit":{"description":"A raw Meilisearch hit for a matching transcript segment or indexed video document.","properties":{"_rankingScore":{"description":"Meilisearch ranking score when returned","nullable":true,"type":"number"},"document_type":{"description":"Indexed document type, such as transcript or attachment","nullable":true,"type":"string"},"duration_ms":{"description":"Segment duration in milliseconds","nullable":true,"type":"integer"},"id":{"description":"Search document ID","type":"string"},"speaker":{"description":"Speaker name when identified, otherwise the diarization label.","nullable":true,"type":"string"},"speaker_label":{"description":"Raw diarization label, or null.","nullable":true,"type":"string"},"speaker_name":{"description":"Resolved speaker name, or null.","nullable":true,"type":"string"},"text":{"description":"Matched transcript text","nullable":true,"type":"string"},"timestamp_end_ms":{"description":"Segment end time in milliseconds","nullable":true,"type":"integer"},"timestamp_start_ms":{"description":"Segment start time in milliseconds","nullable":true,"type":"integer"},"title":{"description":"Source video title","nullable":true,"type":"string"},"video_id":{"description":"Source video UUID","type":"string"}},"title":"SearchHit","type":"object"},"SessionManagementViewerResolveResponse":{"properties":{"data":{"$ref":"#/components/schemas/SessionManagementViewerState"}},"required":["data"],"title":"SessionManagementViewerResolveResponse","type":"object"},"VideoConversationResponse":{"properties":{"conversation_id":{"type":"string"},"created_at":{"format":"date-time","type":"string"},"messages":{"items":{"properties":{"content":{"type":"string"},"id":{"type":"string"},"inserted_at":{"format":"date-time","type":"string"},"role":{"enum":["user","assistant"],"type":"string"},"sources":{"items":{"type":"object"},"type":"array"}},"type":"object"},"type":"array"},"metadata":{"type":"object"},"updated_at":{"format":"date-time","type":"string"},"video_id":{"type":"string"}},"required":["conversation_id","video_id","messages","created_at","updated_at"],"title":"VideoConversationResponse","type":"object"},"RevokeAuthSessionResponse":{"properties":{"ok":{"type":"boolean"},"session_id":{"format":"uuid","type":"string"}},"required":["ok","session_id"],"title":"RevokeAuthSessionResponse","type":"object"},"AiChatResponse":{"description":"JSON response from AI chat (when stream=false)","properties":{"content":{"description":"The AI's response content","type":"string"},"conversation_id":{"description":"Conversation ID for continuing the chat","type":"string"},"id":{"description":"Deprecated: Use conversation_id instead","type":"string"},"model":{"description":"The AI model used","example":"claude-3-5-sonnet-20241022","type":"string"},"usage":{"description":"Token usage statistics","properties":{"input_tokens":{"type":"integer"},"output_tokens":{"type":"integer"}},"type":"object"},"video_id":{"description":"The video's internal ID","type":"string"}},"required":["conversation_id","content","video_id"],"title":"AiChatResponse","type":"object"},"AiChatRequest":{"description":"Request body for AI chat endpoints","example":{"prompt":"What are the key takeaways from this video?","stream":false},"properties":{"conversation_id":{"description":"Existing conversation ID to continue","nullable":true,"type":"string"},"current_time":{"description":"Current video playback position in seconds for contextual answers","nullable":true,"type":"integer"},"images":{"description":"Optional array of images (max 5). Requires the Vision feature flag. Each image can be base64-encoded or a URL.","items":{"anyOf":[{"$ref":"#/components/schemas/AiImageBase64"},{"$ref":"#/components/schemas/AiImageUrl"}]},"maxItems":5,"nullable":true,"type":"array"},"max_tokens":{"description":"Maximum tokens in response","nullable":true,"type":"integer"},"model":{"description":"AI model to use (optional, uses account default)","nullable":true,"type":"string"},"prompt":{"description":"The user's question or prompt","example":"What are the main topics discussed in this video?","type":"string"},"stream":{"default":true,"description":"Whether to stream the response via SSE (default: true)","type":"boolean"},"temperature":{"description":"Temperature for response generation (0.0-2.0)","format":"float","nullable":true,"type":"number"},"viewer":{"description":"Viewer identifier (UUID or your external ID) to associate with the conversation. When the viewers feature is enabled, the viewer's traits also personalize AI responses.","nullable":true,"type":"string"},"viewer_profile":{"anyOf":[{"description":"Free text or markdown describing the viewer; replaces the synced viewer's traits for this request. Up to 8000 characters.","example":"Sarah runs a 3-person agency, pre-launch, wants her first 10 customers.","type":"string"},{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"boolean"}]},"description":"Key-value traits merged over the synced viewer's traits (inline wins per key). Max 20 keys; keys must match ^[a-zA-Z_][a-zA-Z0-9_]*$; values are strings (up to 2000 characters), numbers, or booleans.","example":{"business_name":"Acme Corp","goal":"find first customers","stage":"growth"},"type":"object"}],"description":"Tell the AI about the person asking, for this request only (never persisted). Pass free text/markdown, or a key-value object. Requires the viewers feature.","nullable":true}},"required":["prompt"],"title":"AiChatRequest","type":"object"},"SessionManagementViewerSessionsResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/SessionManagementSessionWithRevocation"},"type":"array"},"viewer":{"$ref":"#/components/schemas/SessionManagementViewerState"}},"required":["viewer","data"],"title":"SessionManagementViewerSessionsResponse","type":"object"},"CustomRedirect":{"description":"A branded shortlink redirect","properties":{"path":{"description":"URL path (e.g., /apple)","example":"/apple","type":"string"},"permanent":{"description":"Whether redirect is permanent (301 vs 307)","example":false,"type":"boolean"},"url":{"description":"Target URL","example":"https://podcasts.apple.com/...","type":"string"}},"required":["path","url","permanent"],"title":"CustomRedirect","type":"object"},"PlaylistResponse":{"description":"Single playlist with videos","properties":{"data":{"$ref":"#/components/schemas/Playlist"}},"required":["data"],"title":"PlaylistResponse","type":"object"},"SessionManagementRevokeAllResponse":{"properties":{"ok":{"type":"boolean"},"revoked_count":{"type":"integer"}},"required":["ok","revoked_count"],"title":"SessionManagementRevokeAllResponse","type":"object"},"ViewerCreate":{"description":"Parameters for creating a viewer","properties":{"avatar_url":{"description":"URL to avatar image","example":"https://example.com/avatars/john.jpg","format":"uri","nullable":true,"type":"string"},"display_name":{"description":"Preferred display name","example":"Johnny D","nullable":true,"type":"string"},"email":{"description":"Viewer email","example":"john@example.com","format":"email","type":"string"},"external_id":{"description":"External ID from course platform","example":"usr_123abc","nullable":true,"type":"string"},"first_name":{"description":"First name","example":"John","nullable":true,"type":"string"},"last_name":{"description":"Last name","example":"Doe","nullable":true,"type":"string"},"name":{"description":"Viewer name","example":"John Doe","type":"string"},"traits":{"description":"Custom traits map","example":{"business_name":"Acme Corp","stage":"growth"},"nullable":true,"type":"object"}},"required":["name"],"title":"ViewerCreate","type":"object"},"AdminViewerSessionsResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/AdminSessionWithRevocation"},"type":"array"}},"required":["data"],"title":"AdminViewerSessionsResponse","type":"object"},"AiSearchResponse":{"description":"Search results with AI synthesis","properties":{"content":{"description":"Synthesized answer","type":"string"},"query":{"description":"Original search query","type":"string"},"sources":{"description":"Source citations","items":{"$ref":"#/components/schemas/Citation"},"type":"array"}},"required":["content","sources","query"],"title":"AiSearchResponse","type":"object"},"ThemeTokens":{"description":"OKLCH color tokens for a theme mode (light or dark)","properties":{"accent":{"description":"Accent color (OKLCH)","type":"string"},"accent_foreground":{"description":"Accent foreground color (OKLCH)","type":"string"},"background":{"description":"Background color (OKLCH)","type":"string"},"border":{"description":"Border color (OKLCH)","type":"string"},"foreground":{"description":"Foreground/text color (OKLCH)","type":"string"},"muted":{"description":"Muted background color (OKLCH)","type":"string"},"muted_foreground":{"description":"Muted text color (OKLCH)","type":"string"},"primary":{"description":"Optional sidebar primary color for buttons/headers (OKLCH)","nullable":true,"type":"string"},"ring":{"description":"Focus ring color (OKLCH)","type":"string"},"sidebar":{"description":"Optional sidebar background color (OKLCH)","nullable":true,"type":"string"},"sidebarAccent":{"description":"Optional sidebar accent color for section headers (OKLCH)","nullable":true,"type":"string"},"surface":{"description":"Surface/card color (OKLCH)","type":"string"}},"title":"ThemeTokens","type":"object"},"ThemeConfig":{"description":"Theme configuration with light and dark mode tokens","properties":{"color_scheme":{"description":"Color scheme preference","enum":["light","dark","toggle"],"type":"string"},"dark":{"$ref":"#/components/schemas/ThemeTokens"},"light":{"$ref":"#/components/schemas/ThemeTokens"},"radius":{"description":"Border radius","example":"0.625rem","type":"string"}},"title":"ThemeConfig","type":"object"},"CommunityAuthor":{"description":"Author information for community posts and comments","properties":{"avatar_url":{"description":"Author avatar URL","example":"https://example.com/avatar.jpg","format":"uri","nullable":true,"type":"string"},"id":{"description":"Author UUID","format":"uuid","type":"string"},"name":{"description":"Author display name","example":"John Doe","type":"string"}},"required":["id","name"],"title":"CommunityAuthor","type":"object"},"ProgressResponse":{"description":"Response containing a single progress record","example":{"data":{"completed":false,"completed_at":null,"created_at":"2026-01-07T12:00:00Z","current_time":300,"duration":1000,"id":"98936fa0-6826-48cc-aac1-89f42235dc8e","percentage":30.0,"updated_at":"2026-01-07T12:05:00Z","video_id":"87654321-4321-4321-4321-987654321cba","viewer_id":"12345678-1234-1234-1234-123456789abc"}},"properties":{"data":{"$ref":"#/components/schemas/Progress"}},"required":["data"],"title":"ProgressResponse","type":"object"},"VideoConversationListResponse":{"properties":{"conversations":{"items":{"$ref":"#/components/schemas/ConversationSummary"},"type":"array"},"pagination":{"$ref":"#/components/schemas/ConversationPagination"},"video_id":{"type":"string"}},"required":["video_id","conversations","pagination"],"title":"VideoConversationListResponse","type":"object"},"AiImageUrl":{"description":"Image referenced by URL (will be fetched and validated)","properties":{"type":{"description":"Image type","enum":["url"],"type":"string"},"url":{"description":"HTTPS URL pointing to an image (max 20MB)","type":"string"}},"required":["type","url"],"title":"AiImageUrl","type":"object"},"AdminSessionManagementExemptionRequest":{"properties":{"exempt":{"description":"Whether this viewer is exempt from session-management enforcement.","type":"boolean"}},"required":["exempt"],"title":"AdminSessionManagementExemptionRequest","type":"object"},"CommunityCommentResponse":{"description":"Response containing a single comment","properties":{"data":{"$ref":"#/components/schemas/CommunityComment"}},"required":["data"],"title":"CommunityCommentResponse","type":"object"},"HeroSettings":{"description":"Hero section configuration","properties":{"type":{"description":"Hero section type","enum":["none","custom"],"example":"none","type":"string"}},"required":["type"],"title":"HeroSettings","type":"object"},"Progress":{"description":"A viewer's progress on a video","example":{"completed":false,"completed_at":null,"created_at":"2026-01-07T12:00:00Z","current_time":300,"duration":1000,"id":"98936fa0-6826-48cc-aac1-89f42235dc8e","percentage":30.0,"updated_at":"2026-01-07T12:05:00Z","video_id":"87654321-4321-4321-4321-987654321cba","viewer_id":"12345678-1234-1234-1234-123456789abc"},"properties":{"completed":{"description":"Whether the video has been completed","type":"boolean"},"completed_at":{"description":"Timestamp when video was completed","format":"date-time","nullable":true,"type":"string"},"created_at":{"description":"Record creation timestamp","format":"date-time","type":"string"},"current_time":{"description":"Current playback position in seconds","minimum":0,"type":"integer"},"duration":{"description":"Total video duration in seconds","minimum":1,"type":"integer"},"id":{"description":"Progress record UUID","format":"uuid","type":"string"},"percentage":{"description":"Completion percentage (0-100)","format":"decimal","type":"number"},"updated_at":{"description":"Record last updated timestamp","format":"date-time","type":"string"},"video_id":{"description":"Video UUID","format":"uuid","type":"string"},"viewer_id":{"description":"Viewer UUID","format":"uuid","type":"string"}},"required":["id","viewer_id","video_id","current_time","duration","percentage","completed"],"title":"Progress","type":"object"},"CommunityPostsResponse":{"description":"Response containing a list of community posts","properties":{"data":{"description":"List of community posts","items":{"$ref":"#/components/schemas/CommunityPost"},"type":"array"},"meta":{"description":"Pagination metadata","properties":{"page":{"description":"Current page number (1-indexed)","example":1,"type":"integer"},"page_size":{"description":"Items per page","example":20,"type":"integer"},"total_entries":{"description":"Total number of items across all pages","example":47,"type":"integer"},"total_pages":{"description":"Total number of pages","example":3,"type":"integer"}},"required":["page","page_size","total_entries","total_pages"],"type":"object"}},"required":["data","meta"],"title":"CommunityPostsResponse","type":"object"},"ProgressUpsert":{"description":"Parameters for creating or updating progress","example":{"progress":{"completed":false,"current_time":500,"duration":1000}},"properties":{"progress":{"properties":{"completed":{"default":false,"description":"Whether the video has been completed","type":"boolean"},"current_time":{"description":"Current playback position in seconds","minimum":0,"type":"integer"},"duration":{"description":"Total video duration in seconds","minimum":1,"type":"integer"}},"required":["current_time","duration"],"type":"object"}},"required":["progress"],"title":"ProgressUpsert","type":"object"},"ApiAuthErrorResponse":{"description":"Authentication failure response returned by API token plugs.","properties":{"error":{"example":"Unauthorized","type":"string"},"status":{"example":401,"type":"integer"},"timestamp":{"example":"2026-04-25 04:00:00Z","type":"string"}},"required":["error","status","timestamp"],"title":"ApiAuthErrorResponse","type":"object"},"AiSearchRequest":{"description":"Search request with optional AI synthesis","example":{"limit":10,"prompt":"authentication setup"},"properties":{"collection_id":{"description":"Filter to a specific collection (legacy single-value filter)","nullable":true,"type":"string"},"collection_ids":{"description":"Filter to multiple collections (OR semantics)","items":{"type":"string"},"nullable":true,"type":"array"},"images":{"description":"Optional array of images (max 5). Requires the Vision feature flag. Each image can be base64-encoded or a URL.","items":{"anyOf":[{"$ref":"#/components/schemas/AiImageBase64"},{"$ref":"#/components/schemas/AiImageUrl"}]},"maxItems":5,"nullable":true,"type":"array"},"limit":{"description":"Maximum sources to return (default: 15)","example":10,"type":"integer"},"prompt":{"description":"Search query","example":"How do I set up authentication?","type":"string"},"stream":{"default":true,"description":"Stream response via SSE (default: true)","type":"boolean"},"tags":{"description":"Filter by video tags","items":{"type":"string"},"nullable":true,"type":"array"},"video_id":{"description":"Filter to a specific video","nullable":true,"type":"string"}},"required":["prompt"],"title":"AiSearchRequest","type":"object"},"ConversationResponse":{"description":"Full conversation history","properties":{"conversation_id":{"description":"Conversation ID","type":"string"},"created_at":{"format":"date-time","type":"string"},"messages":{"description":"Message history","items":{"properties":{"content":{"type":"string"},"role":{"enum":["user","assistant"],"type":"string"}},"type":"object"},"type":"array"},"updated_at":{"format":"date-time","type":"string"}},"required":["conversation_id","messages"],"title":"ConversationResponse","type":"object"},"CommunityComment":{"description":"A comment on a community post","properties":{"author":{"$ref":"#/components/schemas/CommunityAuthor"},"content":{"description":"Comment content","example":"Great post! Thanks for sharing.","type":"string"},"created_at":{"description":"Creation timestamp","format":"date-time","type":"string"},"depth":{"description":"Nesting depth (0 = top-level, max 2)","example":0,"type":"integer"},"id":{"description":"Comment UUID","format":"uuid","type":"string"},"reactions":{"description":"Reactions summary (mirrors the post shape, without reacted_by)","properties":{"count":{"description":"Total number of reactions","example":5,"type":"integer"},"viewer_has_reacted":{"description":"Whether the current viewer has reacted (only when X-Viewer-ID header is provided)","nullable":true,"type":"boolean"}},"required":["count"],"type":"object"},"replies":{"description":"Nested replies (same structure as comment)","items":{"type":"object"},"type":"array"},"updated_at":{"description":"Last update timestamp","format":"date-time","type":"string"}},"required":["id","content","depth","reactions","author","created_at"],"title":"CommunityComment","type":"object"},"ConversationPagination":{"properties":{"has_more":{"type":"boolean"},"limit":{"type":"integer"},"offset":{"type":"integer"}},"required":["limit","offset","has_more"],"title":"ConversationPagination","type":"object"},"AdminSessionManagementExemptionResponse":{"properties":{"data":{"properties":{"device_limit_override":{"nullable":true,"type":"integer"},"session_management_exempt":{"type":"boolean"},"viewer_id":{"format":"uuid","type":"string"}},"required":["viewer_id","session_management_exempt"],"type":"object"}},"required":["data"],"title":"AdminSessionManagementExemptionResponse","type":"object"},"SessionManagementRevokeSessionResponse":{"properties":{"ok":{"type":"boolean"},"session_id":{"format":"uuid","type":"string"}},"required":["ok","session_id"],"title":"SessionManagementRevokeSessionResponse","type":"object"},"ConversationVideoContext":{"description":"Lightweight video metadata for conversation listings","properties":{"id":{"description":"Video external ID (hashid or UUID per account settings)","type":"string"},"thumbnail_url":{"description":"Video thumbnail URL","nullable":true,"type":"string"},"title":{"description":"Video title","type":"string"},"url":{"description":"Video page URL on portal","nullable":true,"type":"string"}},"required":["id","title"],"title":"ConversationVideoContext","type":"object"},"ProgressListResponse":{"description":"Response containing a list of progress records with metadata","example":{"data":[{"completed":false,"completed_at":null,"created_at":"2026-01-07T12:00:00Z","current_time":300,"duration":1000,"id":"98936fa0-6826-48cc-aac1-89f42235dc8e","percentage":30.0,"updated_at":"2026-01-07T12:05:00Z","video_id":"87654321-4321-4321-4321-987654321cba","viewer_id":"12345678-1234-1234-1234-123456789abc"}],"meta":{"completed":3,"in_progress":7,"total":10}},"properties":{"data":{"description":"List of progress records","items":{"$ref":"#/components/schemas/Progress"},"type":"array"},"meta":{"properties":{"completed":{"description":"Number of completed videos","type":"integer"},"in_progress":{"description":"Number of videos in progress","type":"integer"},"total":{"description":"Total number of progress records","type":"integer"}},"type":"object"}},"required":["data","meta"],"title":"ProgressListResponse","type":"object"},"ViewerMemory":{"description":"A viewer's AI-maintained memory document, distilled from their conversations.","properties":{"content":{"description":"The distilled memory document. AI-maintained; cleared via DELETE; regrows from future conversations unless `enabled` is false. Null when nothing has been distilled yet.","example":"Situation & goals:\n- Runs a 3-person agency scaling to $1M ARR.","nullable":true,"type":"string"},"conversations_count":{"description":"Number of summarized conversations that fed this memory.","example":4,"type":"integer"},"enabled":{"description":"Whether memory distillation is active for this viewer. Set false via PATCH to opt out (re-distillation stops; existing content is retained until cleared).","example":true,"type":"boolean"},"last_conversation_at":{"description":"Timestamp of the most recent conversation activity.","format":"date-time","nullable":true,"type":"string"},"last_distilled_at":{"description":"When the memory document was last rewritten.","format":"date-time","nullable":true,"type":"string"},"updated_at":{"description":"Last update timestamp for the memory row.","format":"date-time","nullable":true,"type":"string"}},"required":["content","enabled","conversations_count"],"title":"ViewerMemory","type":"object"},"Error":{"description":"API error response","properties":{"code":{"description":"Machine-readable error code","example":"not_found","type":"string"},"message":{"description":"Human-readable error message","example":"Video not found","type":"string"},"retryable":{"description":"Whether the request can be retried","example":false,"type":"boolean"},"type":{"description":"Error type","example":"error","type":"string"}},"required":["code","message"],"title":"Error","type":"object"},"AdminViewerResolveResponse":{"properties":{"data":{"properties":{"device_limit_override":{"nullable":true,"type":"integer"},"external_id":{"type":"string"},"session_management_exempt":{"type":"boolean"},"viewer_id":{"format":"uuid","type":"string"}},"required":["viewer_id","external_id","session_management_exempt"],"type":"object"}},"required":["data"],"title":"AdminViewerResolveResponse","type":"object"},"CollectionsResponse":{"description":"Response containing a list of collections","properties":{"data":{"description":"List of collections","items":{"$ref":"#/components/schemas/Collection"},"type":"array"}},"required":["data"],"title":"CollectionsResponse","type":"object"},"SearchErrorResponse":{"description":"Search failure response returned by the search controller.","properties":{"details":{"description":"Search backend error details"},"error":{"example":"Search failed","type":"string"}},"required":["error","details"],"title":"SearchErrorResponse","type":"object"}},"securitySchemes":{"admin_api_token":{"description":"Privileged admin key (bold_live_* with admin: true) for Mission Control and support automation. Do not put this key in customer-owned apps.","in":"header","name":"X-Bold-Admin-Api-Key","type":"apiKey"},"api_token":{"description":"API token provided by BOLD Admin","scheme":"bearer","type":"http"},"upstream_jwt":{"bearerFormat":"JWT","description":"Customer IdP JWT used by session-management client endpoints. Must match the tenant policy's issuer/audience/JWKS settings.","scheme":"bearer","type":"http"}}},"info":{"description":"API for accessing video content, collections, playlists, and AI-powered features.\n\n## Authentication\n\nMost API endpoints require authentication via a BOLD API token. Include your token\nin the `Authorization` header as a bearer token:\n\n```\nAuthorization: Bearer YOUR_API_TOKEN\n```\n\nSession-management client endpoints under `/api/v1/auth/*` use the customer's\nupstream IdP JWT instead:\n\n```\nAuthorization: Bearer UPSTREAM_IDP_JWT\nX-Bold-Tenant-Slug: YOUR_TENANT_SLUG\n```\n\nServer-side session-management integration endpoints under\n`/api/v1/session-management/*` use the normal tenant BOLD API token. These\nendpoints are for customer-owned servers to inspect and revoke device sessions\nafter the customer app has already decided the viewer is allowed to access content.\n\nAdmin endpoints under `/api/v1/admin/*` require `X-Bold-Admin-Api-Key` and are\nreserved for Mission Control or deliberately privileged support automation.\n","title":"BoldAdmin API","version":"1.0.0"},"openapi":"3.0.0","paths":{"/api/v1/auth/challenges/{id}/verify":{"post":{"callbacks":{},"description":"Verifies a short-lived email code for the authenticated viewer and materializes the pending auth session if the code is valid.","operationId":"BoldAdminWeb.Api.Auth.ChallengeController.verify","parameters":[{"description":"Challenge UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Tenant slug for the BOLD account, for example `hrtu`.","in":"header","name":"X-Bold-Tenant-Slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyAuthChallengeRequest"}}},"description":"Challenge verification payload","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSessionResponse"}}},"description":"Session created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthChallengeError"}}},"description":"Invalid, expired, or incorrect challenge code"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthChallengeError"}}},"description":"Invalid bearer JWT"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthChallengeError"}}},"description":"Challenge ownership mismatch"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthChallengeError"}}},"description":"Viewer not provisioned"}},"security":[{"upstream_jwt":[]}],"summary":"Verify an auth challenge code","tags":["Auth Challenges"]}},"/api/v1/settings":{"get":{"callbacks":{},"description":"Returns public configuration for your video portal.\n\nIncludes branding (logos, colors), AI settings, and portal layout configuration.\nUse this to customize embedded players or build custom portal UIs.\n","operationId":"BoldAdminWeb.Api.ProjectSettingsController.settings (2)","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectSettings"}}},"description":"Settings"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"}},"summary":"Get portal settings","tags":["Settings"]}},"/api/v1/viewers/{viewer_id}/progress":{"get":{"callbacks":{},"description":"Returns all progress records for a viewer with metadata statistics.","operationId":"BoldAdminWeb.Api.ProgressController.index","parameters":[{"description":"Viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}},{"description":"Filter by completion status","in":"query","name":"completed","required":false,"schema":{"type":"boolean"}},{"description":"Filter by collection UUID","in":"query","name":"collection_id","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgressListResponse"}}},"description":"Progress list"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers feature is not enabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer not found"}},"summary":"List viewer progress","tags":["Progress"]}},"/api/v1/playlists/{id}":{"get":{"callbacks":{},"description":"Returns a playlist with its videos.","operationId":"BoldAdminWeb.Api.PlaylistController.show","parameters":[{"description":"Playlist ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaylistResponse"}}},"description":"Playlist"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Playlist not found"}},"summary":"Get a playlist","tags":["Playlists"]}},"/api/v1/viewers/{viewer_id}/progress/{video_id}":{"delete":{"callbacks":{},"description":"Resets progress by deleting the progress record. This allows the viewer to start fresh on the video.","operationId":"BoldAdminWeb.Api.ProgressController.delete","parameters":[{"description":"Viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}},{"description":"Video UUID","in":"path","name":"video_id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"content":{"application/json":{}},"description":"Progress reset"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers feature is not enabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Progress not found"}},"summary":"Reset progress","tags":["Progress"]},"get":{"callbacks":{},"description":"Returns progress for a specific viewer-video pair.","operationId":"BoldAdminWeb.Api.ProgressController.show","parameters":[{"description":"Viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}},{"description":"Video UUID","in":"path","name":"video_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgressResponse"}}},"description":"Progress details"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers feature is not enabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Progress not found"}},"summary":"Get progress for a video","tags":["Progress"]},"post":{"callbacks":{},"description":"Creates or updates progress for a viewer-video pair. If progress exists, it will be updated; otherwise, a new record will be created.","operationId":"BoldAdminWeb.Api.ProgressController.upsert","parameters":[{"description":"Viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}},{"description":"Video UUID","in":"path","name":"video_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgressUpsert"}}},"description":"Progress parameters","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgressResponse"}}},"description":"Progress updated"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgressResponse"}}},"description":"Progress created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid parameters"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers feature is not enabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer or video not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"summary":"Create or update progress","tags":["Progress"]}},"/api/v1/viewers/{viewer_id}/progress/{video_id}/complete":{"post":{"callbacks":{},"description":"Marks a video as completed for the viewer. Sets completed=true, current_time=duration, and records completion timestamp. Progress must already exist.","operationId":"BoldAdminWeb.Api.ProgressController.complete","parameters":[{"description":"Viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}},{"description":"Video UUID","in":"path","name":"video_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgressResponse"}}},"description":"Video marked as complete"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers feature is not enabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Progress not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"summary":"Mark video as complete","tags":["Progress"]}},"/api/v1/auth/sessions/revoke-others":{"post":{"callbacks":{},"operationId":"BoldAdminWeb.Api.Auth.SessionController.revoke_others","parameters":[{"description":"Tenant slug for the BOLD account, for example `hrtu`.","in":"header","name":"X-Bold-Tenant-Slug","required":true,"schema":{"type":"string"}},{"description":"ID of the session to keep","in":"header","name":"x-bold-session-id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeOthersResponse"}}},"description":"Other sessions revoked"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing X-Bold-Session-Id header"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid bearer JWT"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SelfManagementDisabledError"}}},"description":"Self-management disabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer or keep-session not found"}},"security":[{"upstream_jwt":[]}],"summary":"Revoke all of the authenticated viewer's sessions except the current one","tags":["Auth Sessions"]}},"/api/v1/videos/latest":{"get":{"callbacks":{},"description":"Returns the most recently published public videos.\n\nWhen `viewer_id` is provided, each video includes a `progress` field showing the viewer's\nwatch progress (current_time, duration, percentage, completed status). Videos without\nprogress for that viewer will have `progress: null`.\n","operationId":"BoldAdminWeb.Api.VideoController.latest","parameters":[{"description":"Maximum number of videos (default: 6, max: 50)","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"Filter by tag","in":"query","name":"tag","required":false,"schema":{"type":"string"}},{"description":"Filter by collection ID","in":"query","name":"collection_id","required":false,"schema":{"type":"string"}},{"description":"Filter by multiple collection IDs (OR semantics). Supports repeated query params and comma-separated values.","explode":true,"in":"query","name":"collection_ids","required":false,"schema":{"items":{"type":"string"},"type":"array"},"style":"form"},{"description":"Viewer UUID to include watch progress","in":"query","name":"viewer_id","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideosResponse"}}},"description":"Videos list"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"}},"summary":"Get latest videos","tags":["Videos"]}},"/api/v1/community/posts/{id}":{"delete":{"callbacks":{},"description":"Deletes an existing community post. Requires the `X-Viewer-ID` header.\n\nOnly the post author or an admin viewer can delete a post.\n","operationId":"BoldAdminWeb.Api.CommunityController.delete","parameters":[{"description":"Post UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Viewer UUID (required)","in":"header","name":"X-Viewer-ID","required":true,"schema":{"type":"string"}}],"responses":{"204":{"content":{"application/json":{}},"description":"Post deleted"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Forbidden - not post owner or admin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Post not found"}},"summary":"Delete a community post","tags":["Community"]},"get":{"callbacks":{},"description":"Returns a single community post by ID with nested comments.\n\nPass the `X-Viewer-ID` header to include `viewer_has_reacted` booleans in the response.\n","operationId":"BoldAdminWeb.Api.CommunityController.show","parameters":[{"description":"Post UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Viewer UUID for reaction status","in":"header","name":"X-Viewer-ID","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPostResponse"}}},"description":"Community post details"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Post not found"}},"summary":"Get a community post","tags":["Community"]},"patch":{"callbacks":{},"description":"Updates an existing community post. Requires the `X-Viewer-ID` header.\n\nOnly the post author or an admin viewer can update a post.\n","operationId":"BoldAdminWeb.Api.CommunityController.update (2)","parameters":[{"description":"Post UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Viewer UUID (required)","in":"header","name":"X-Viewer-ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPostCreate"}}},"description":"Post parameters","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPostResponse"}}},"description":"Post updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid parameters"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Forbidden - not post owner or admin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Post not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"summary":"Update a community post","tags":["Community"]},"put":{"callbacks":{},"description":"Updates an existing community post. Requires the `X-Viewer-ID` header.\n\nOnly the post author or an admin viewer can update a post.\n","operationId":"BoldAdminWeb.Api.CommunityController.update","parameters":[{"description":"Post UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Viewer UUID (required)","in":"header","name":"X-Viewer-ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPostCreate"}}},"description":"Post parameters","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPostResponse"}}},"description":"Post updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid parameters"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Forbidden - not post owner or admin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Post not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"summary":"Update a community post","tags":["Community"]}},"/api/v1/community/comments/{id}":{"delete":{"callbacks":{},"description":"Deletes an existing comment. Requires the `X-Viewer-ID` header.\n\nOnly the comment author or an admin viewer can delete a comment.\n","operationId":"BoldAdminWeb.Api.CommunityController.delete_comment","parameters":[{"description":"Comment UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Viewer UUID (required)","in":"header","name":"X-Viewer-ID","required":true,"schema":{"type":"string"}}],"responses":{"204":{"content":{"application/json":{}},"description":"Comment deleted"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Forbidden - not comment owner or admin"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Comment not found"}},"summary":"Delete a comment","tags":["Community"]}},"/api/v1/session-management/viewers/by-external-id/{external_id}":{"get":{"callbacks":{},"description":"Tenant-scoped customer-server endpoint. The normal BOLD tenant API key determines the account; no admin API key is required.","operationId":"BoldAdminWeb.Api.SessionManagement.ViewerController.by_external_id","parameters":[{"description":"Optional tenant slug. When provided, it must match the tenant API key's account slug.","in":"header","name":"X-Bold-Tenant-Slug","required":false,"schema":{"type":"string"}},{"description":"Upstream IdP user ID","in":"path","name":"external_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionManagementViewerResolveResponse"}}},"description":"Resolved viewer"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Session management unavailable or account inactive"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Viewer not found"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Auth policy unavailable"}},"security":[{"api_token":[]}],"summary":"Resolve an upstream user ID to a BOLD viewer","tags":["Session Management"]}},"/api/v1/auth/challenges/{id}/resend":{"post":{"callbacks":{},"description":"Generates and emails a fresh verification code for an active challenge. The original challenge expiry is preserved and only the latest emailed code is valid.","operationId":"BoldAdminWeb.Api.Auth.ChallengeController.resend","parameters":[{"description":"Challenge UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Tenant slug for the BOLD account, for example `hrtu`.","in":"header","name":"X-Bold-Tenant-Slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthChallengeResendResponse"}}},"description":"Challenge code resent"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthChallengeError"}}},"description":"Invalid, expired, or resend-limited challenge"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthChallengeError"}}},"description":"Invalid bearer JWT"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthChallengeError"}}},"description":"Challenge ownership mismatch"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthChallengeError"}}},"description":"Viewer not provisioned"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthChallengeError"}}},"description":"Viewer email required"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthChallengeError"}}},"description":"Resend rate limited"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthChallengeError"}}},"description":"Challenge email delivery unavailable"}},"security":[{"upstream_jwt":[]}],"summary":"Resend an auth challenge code","tags":["Auth Challenges"]}},"/api/v1/community/posts":{"get":{"callbacks":{},"description":"Returns community posts for the current tenant with optional filtering and pagination.\n\nPosts are ordered by: pinned first, then by pin timestamp, then by creation date.\n\nPass the `X-Viewer-ID` header to include `viewer_has_reacted` boolean in each post's reactions.\n","operationId":"BoldAdminWeb.Api.CommunityController.index","parameters":[{"description":"Filter by category","example":"announcements","in":"query","name":"category","required":false,"schema":{"type":"string"}},{"description":"Page number (default: 1)","example":1,"in":"query","name":"page","required":false,"schema":{"type":"integer"}},{"description":"Items per page (default: 20, max: 100)","example":20,"in":"query","name":"page_size","required":false,"schema":{"type":"integer"}},{"description":"Viewer UUID for reaction status","in":"header","name":"X-Viewer-ID","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPostsResponse"}}},"description":"Community posts list"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"}},"summary":"List community posts","tags":["Community"]},"post":{"callbacks":{},"description":"Creates a new community post. Requires the `X-Viewer-ID` header to identify the author.\n\nThe viewer must exist in the system before creating a post.\n","operationId":"BoldAdminWeb.Api.CommunityController.create","parameters":[{"description":"Viewer UUID (required)","in":"header","name":"X-Viewer-ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPostCreate"}}},"description":"Post parameters","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPostResponse"}}},"description":"Post created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid parameters"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized - X-Viewer-ID header required"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"summary":"Create a community post","tags":["Community"]}},"/api/v1/community/posts/{id}/react":{"post":{"callbacks":{},"description":"Toggles the current viewer's reaction on a post. If the viewer has already\nreacted, the reaction is removed. If not, a reaction is added.\n\nRequires the `X-Viewer-ID` header.\n","operationId":"BoldAdminWeb.Api.CommunityController.react_to_post","parameters":[{"description":"Post UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Viewer UUID (required)","in":"header","name":"X-Viewer-ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionResponse"}}},"description":"Reaction toggled"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Post not found"}},"summary":"Toggle reaction on a post","tags":["Community"]}},"/api/v1/collections/{id}":{"get":{"callbacks":{},"description":"Returns a single collection by ID or slug.","operationId":"BoldAdminWeb.Api.CollectionController.show","parameters":[{"description":"Collection UUID or slug","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Include 'children' for direct children or 'descendants' for full tree","in":"query","name":"include","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionResponse"}}},"description":"Collection details"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Collection not found"}},"summary":"Get a collection","tags":["Collections"]}},"/api/v1/admin/viewers/{viewer_id}/session-management-exemption":{"put":{"callbacks":{},"description":"Marks whether a viewer is exempt from device-limit enforcement. This is distinct from numeric device_limit_override.","operationId":"BoldAdminWeb.Api.Admin.ViewerController.update_session_management_exemption","parameters":[{"description":"Bold viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSessionManagementExemptionRequest"}}},"description":"Exemption value","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminSessionManagementExemptionResponse"}}},"description":"Exemption updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Invalid request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Viewer not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Invalid value"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Tenant migration unavailable"}},"security":[{"admin_api_token":[]}],"summary":"Set or clear a viewer's session-management exemption","tags":["Admin Viewers"]}},"/api/v1/session-management/viewers/by-external-id/{external_id}/sessions/revoke-all":{"post":{"callbacks":{},"operationId":"BoldAdminWeb.Api.SessionManagement.ViewerSessionController.revoke_all","parameters":[{"description":"Optional tenant slug. When provided, it must match the tenant API key's account slug.","in":"header","name":"X-Bold-Tenant-Slug","required":false,"schema":{"type":"string"}},{"description":"Upstream IdP user ID","in":"path","name":"external_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionManagementRevokeSessionRequest"}}},"description":"Optional reason","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionManagementRevokeAllResponse"}}},"description":"All sessions revoked"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Invalid reason"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Session management unavailable or account inactive"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Viewer not found"}},"security":[{"api_token":[]}],"summary":"Revoke every active session for a viewer by upstream user ID","tags":["Session Management"]}},"/api/v1/ai/search":{"post":{"callbacks":{},"description":"Semantic search across your video library with AI-synthesized results.\n\nReturns relevant transcript segments and an AI-generated summary.\nFaster than full chat—use this for search experiences.\n\n**Streaming (default):** Returns SSE events:\n- `message_start`: Stream started\n- `sources`: Matching transcript segments\n- `text_delta`: Synthesis text chunk\n- `message_complete`: Complete response\n\nSet `stream=false` for a single JSON response.\n","operationId":"BoldAdminWeb.Api.AiSearchController.search","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiSearchRequest"}}},"description":"Search request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiSearchResponse"}}},"description":"Search results"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid query"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"AI not enabled"}},"summary":"Search with AI synthesis","tags":["AI Search"]}},"/api/v1/community/comments/{id}/react":{"post":{"callbacks":{},"description":"Toggles the current viewer's reaction on a comment. If the viewer has already\nreacted, the reaction is removed. If not, a reaction is added.\n\nRequires the `X-Viewer-ID` header.\n","operationId":"BoldAdminWeb.Api.CommunityController.react_to_comment","parameters":[{"description":"Comment UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Viewer UUID (required)","in":"header","name":"X-Viewer-ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionResponse"}}},"description":"Reaction toggled"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Comment not found"}},"summary":"Toggle reaction on a comment","tags":["Community"]}},"/api/v1/ai/recommendations":{"post":{"callbacks":{},"description":"Get AI-powered video recommendations based on topics or learning goals.\n\nOptionally includes guidance text explaining why each video was recommended.\n\n**Streaming (default):** Returns SSE events:\n- `message_start`: Stream started\n- `text_delta`: Guidance text chunk\n- `recommendations`: Video list\n- `message_complete`: Complete response\n\nSet `stream=false` for a single JSON response.\n","operationId":"BoldAdminWeb.Api.RecommendController.recommend","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiRecommendationsRequest"}}},"description":"Recommendations request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiRecommendationsResponse"}}},"description":"Recommendations"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid topics"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"AI not enabled"}},"summary":"Get video recommendations","tags":["AI Recommendations"]}},"/api/v1/session-management/viewers/by-external-id/{external_id}/sessions":{"get":{"callbacks":{},"description":"Tenant-scoped customer-server endpoint. Returns the resolved viewer's computed session-management state plus active and recently revoked sessions.","operationId":"BoldAdminWeb.Api.SessionManagement.ViewerSessionController.index","parameters":[{"description":"Optional tenant slug. When provided, it must match the tenant API key's account slug.","in":"header","name":"X-Bold-Tenant-Slug","required":false,"schema":{"type":"string"}},{"description":"Upstream IdP user ID","in":"path","name":"external_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionManagementViewerSessionsResponse"}}},"description":"Sessions for viewer"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Session management unavailable or account inactive"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Viewer not found"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Auth policy unavailable"}},"security":[{"api_token":[]}],"summary":"List sessions for a viewer by upstream user ID","tags":["Session Management"]}},"/api/v1/ai/videos/{id}/chat/{conversation_id}":{"delete":{"callbacks":{},"description":"Delete a video chat conversation and all associated messages.","operationId":"BoldAdminWeb.Api.AiController.delete","parameters":[{"description":"Video ID or slug","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Conversation ID","in":"path","name":"conversation_id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"content":{"application/json":{}},"description":"Conversation deleted"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Conversation/video mismatch"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"AI feature not available"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Video or conversation not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Deletion failed"}},"summary":"Delete a video chat conversation","tags":["AI Chat"]},"get":{"callbacks":{},"description":"Retrieve the full message history for a video chat conversation.","operationId":"BoldAdminWeb.Api.AiController.show","parameters":[{"description":"Video ID or slug","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Conversation ID","in":"path","name":"conversation_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoConversationResponse"}}},"description":"Conversation"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Conversation/video mismatch"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"AI feature not available"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Video or conversation not found"}},"summary":"Get a video chat conversation","tags":["AI Chat"]},"post":{"callbacks":{},"description":"Continue an existing conversation about a video.","operationId":"BoldAdminWeb.Api.AiController.continue_chat","parameters":[{"description":"Video ID or slug","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Existing conversation ID","in":"path","name":"conversation_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiChatRequest"}}},"description":"Chat request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiChatResponse"}}},"description":"Chat response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid request or conversation mismatch"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"AI feature not available"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Video, transcript, or conversation not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Processing failed"}},"summary":"Continue a video chat conversation","tags":["AI Chat"]}},"/api/v1/admin/viewers/{viewer_id}/sessions":{"get":{"callbacks":{},"operationId":"BoldAdminWeb.Api.Admin.ViewerSessionController.index","parameters":[{"description":"Viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminViewerSessionsResponse"}}},"description":"Sessions for viewer"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer not found"}},"security":[{"admin_api_token":[]}],"summary":"List active + last-30-days revoked sessions for a viewer","tags":["Admin Auth Sessions"]}},"/api/v1/admin/viewers/{viewer_id}/sessions/revoke-all":{"post":{"callbacks":{},"operationId":"BoldAdminWeb.Api.Admin.ViewerSessionController.revoke_all","parameters":[{"description":"Viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminRevokeAuthSessionRequest"}}},"description":"Optional reason","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminRevokeAllResponse"}}},"description":"All sessions revoked"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer not found"}},"security":[{"admin_api_token":[]}],"summary":"Admin-revoke every active session for a viewer","tags":["Admin Auth Sessions"]}},"/api/v1/session-management/viewers/by-external-id/{external_id}/device-limit":{"put":{"callbacks":{},"description":"Tenant-scoped customer-server endpoint. Mutates only the resolved viewer's device-limit override when per-user admin overrides are enabled for the account.","operationId":"BoldAdminWeb.Api.SessionManagement.ViewerController.update_device_limit","parameters":[{"description":"Optional tenant slug. When provided, it must match the tenant API key's account slug.","in":"header","name":"X-Bold-Tenant-Slug","required":false,"schema":{"type":"string"}},{"description":"Upstream IdP user ID","in":"path","name":"external_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionManagementDeviceLimitRequest"}}},"description":"Limit value","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionManagementViewerStateResponse"}}},"description":"Viewer state"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Invalid request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Customer-admin overrides disabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Viewer not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Invalid limit"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Auth policy unavailable"}},"security":[{"api_token":[]}],"summary":"Set or clear a viewer's device-limit override by upstream user ID","tags":["Session Management"]}},"/api/v1/admin/viewers/by-external-id/{external_id}":{"get":{"callbacks":{},"operationId":"BoldAdminWeb.Api.Admin.ViewerController.by_external_id","parameters":[{"description":"Upstream IdP user ID","in":"path","name":"external_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminViewerResolveResponse"}}},"description":"Resolved viewer"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Viewer not found"}},"security":[{"admin_api_token":[]}],"summary":"Resolve an upstream IdP user ID to a Bold viewer_id","tags":["Admin Viewers"]}},"/api/v1/session-management/viewers/by-external-id/{external_id}/sessions/{id}/revoke":{"post":{"callbacks":{},"operationId":"BoldAdminWeb.Api.SessionManagement.ViewerSessionController.revoke","parameters":[{"description":"Optional tenant slug. When provided, it must match the tenant API key's account slug.","in":"header","name":"X-Bold-Tenant-Slug","required":false,"schema":{"type":"string"}},{"description":"Upstream IdP user ID","in":"path","name":"external_id","required":true,"schema":{"type":"string"}},{"description":"Session UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionManagementRevokeSessionRequest"}}},"description":"Optional reason","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionManagementRevokeSessionResponse"}}},"description":"Session revoked"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Invalid reason"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Session management unavailable or account inactive"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Viewer or session not found"}},"security":[{"api_token":[]}],"summary":"Revoke a viewer session by upstream user ID","tags":["Session Management"]}},"/api/v1/session-management/viewers/by-external-id/{external_id}/session-management-exemption":{"put":{"callbacks":{},"description":"Tenant-scoped customer-server endpoint. Marks whether the resolved viewer is exempt from session-management enforcement when per-user admin overrides are enabled.","operationId":"BoldAdminWeb.Api.SessionManagement.ViewerController.update_session_management_exemption","parameters":[{"description":"Optional tenant slug. When provided, it must match the tenant API key's account slug.","in":"header","name":"X-Bold-Tenant-Slug","required":false,"schema":{"type":"string"}},{"description":"Upstream IdP user ID","in":"path","name":"external_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionManagementExemptionRequest"}}},"description":"Exemption value","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionManagementViewerStateResponse"}}},"description":"Viewer state"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Invalid request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Customer-admin overrides disabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Viewer not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Invalid value"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Auth policy or tenant migration unavailable"}},"security":[{"api_token":[]}],"summary":"Set or clear a viewer's session-management exemption by upstream user ID","tags":["Session Management"]}},"/api/v1/auth/sessions/{id}/revoke":{"post":{"callbacks":{},"operationId":"BoldAdminWeb.Api.Auth.SessionController.revoke","parameters":[{"description":"Session UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Tenant slug for the BOLD account, for example `hrtu`.","in":"header","name":"X-Bold-Tenant-Slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeAuthSessionResponse"}}},"description":"Session revoked"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid bearer JWT"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Self-management disabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Session not found or not owned by viewer"}},"security":[{"upstream_jwt":[]}],"summary":"Self-revoke one of the authenticated viewer's sessions","tags":["Auth Sessions"]}},"/api/v1/viewers/{viewer_id}/memory":{"delete":{"callbacks":{},"description":"Clears the viewer's memory content (idempotent). The document regrows from future conversations unless the viewer is also opted out via PATCH `{\"memory\": {\"enabled\": false}}`. Requires the `viewer_memory` feature.","operationId":"BoldAdminWeb.Api.ViewerMemoryController.delete","parameters":[{"description":"Viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"content":{"application/json":{}},"description":"Memory cleared"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers/viewer_memory feature is not enabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer not found"}},"summary":"Clear a viewer's memory","tags":["Viewers"]},"get":{"callbacks":{},"description":"Returns the AI-maintained memory document for a viewer. If nothing has been distilled yet, returns an empty default (content null, enabled true). Requires the `viewer_memory` feature.","operationId":"BoldAdminWeb.Api.ViewerMemoryController.show","parameters":[{"description":"Viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewerMemoryResponse"}}},"description":"Viewer memory"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers/viewer_memory feature is not enabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer not found"}},"summary":"Get a viewer's memory","tags":["Viewers"]},"patch":{"callbacks":{},"description":"Opts a viewer in or out of memory re-distillation via `{\"memory\": {\"enabled\": bool}}`. Opting out stops future distillation but retains existing content until cleared. Requires the `viewer_memory` feature.","operationId":"BoldAdminWeb.Api.ViewerMemoryController.update","parameters":[{"description":"Viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewerMemoryUpdate"}}},"description":"Memory settings","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewerMemoryResponse"}}},"description":"Updated viewer memory"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid parameters"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers/viewer_memory feature is not enabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"summary":"Update a viewer's memory settings","tags":["Viewers"]}},"/api/v1/auth/sessions/{id}/verify":{"get":{"callbacks":{},"operationId":"BoldAdminWeb.Api.Auth.SessionController.verify","parameters":[{"description":"Session UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Tenant slug for the BOLD account, for example `hrtu`.","in":"header","name":"X-Bold-Tenant-Slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyAuthSessionResponse"}}},"description":"Verification result"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid bearer JWT"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Ownership mismatch or account inactive"}},"security":[{"upstream_jwt":[]}],"summary":"Verify a session is still valid","tags":["Auth Sessions"]}},"/api/v1/admin/viewers/{viewer_id}/device-limit":{"put":{"callbacks":{},"operationId":"BoldAdminWeb.Api.Admin.ViewerController.update_device_limit","parameters":[{"description":"Bold viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminDeviceLimitRequest"}}},"description":"Limit value","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminDeviceLimitResponse"}}},"description":"Override updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Invalid request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Viewer not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Invalid limit"}},"security":[{"admin_api_token":[]}],"summary":"Set or clear a viewer's device-limit override","tags":["Admin Viewers"]}},"/api/v1/collections":{"get":{"callbacks":{},"description":"Returns collections for the current tenant. Use query params to filter.\n\nIf both `root_only` and `parent_id` are provided, `root_only` takes precedence.\n","operationId":"BoldAdminWeb.Api.CollectionController.index","parameters":[{"description":"Only return root-level collections (no parent)","in":"query","name":"root_only","required":false,"schema":{"type":"boolean"}},{"description":"Filter by parent collection UUID","in":"query","name":"parent_id","required":false,"schema":{"type":"string"}},{"description":"Maximum number of collections (1-1000, default: 50)","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"Offset for pagination (default: 0)","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionsResponse"}}},"description":"Collections list"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid parent_id format"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"}},"summary":"List collections","tags":["Collections"]}},"/api/v1/collections/{collection_id}/videos":{"get":{"callbacks":{},"description":"Returns all videos belonging to a collection.","operationId":"BoldAdminWeb.Api.CollectionController.videos","parameters":[{"description":"Collection UUID or slug","in":"path","name":"collection_id","required":true,"schema":{"type":"string"}},{"description":"Maximum number of videos (1-1000, default: 100)","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"Offset for pagination (default: 0)","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionVideosResponse"}}},"description":"Collection videos"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid parameters"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Collection not found"}},"summary":"List videos in a collection","tags":["Collections"]}},"/api/v1/videos/{id}":{"get":{"callbacks":{},"description":"Returns a single video by hashid, UUID, or slug.\n\n**Lookup order:** UUID → hashid → slug.\n","operationId":"BoldAdminWeb.Api.VideoController.show","parameters":[{"description":"Video ID (hashid, UUID) or slug","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoResponse"}}},"description":"Video details"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Video not found"}},"summary":"Get a video","tags":["Videos"]}},"/api/v1/search":{"get":{"callbacks":{},"description":"Searches public, published video transcript and indexed attachment content.\n\nThis endpoint is shared by public API clients and the hosted portal runtime.\nServer-side filters always restrict results to public videos published before the request time.\n","operationId":"BoldAdminWeb.Shared.SearchController.search (2)","parameters":[{"description":"Search query","in":"query","name":"query","required":false,"schema":{"type":"string"}},{"description":"Page number (default: 1)","in":"query","name":"page","required":false,"schema":{"type":"integer"}},{"description":"Results per page (default: 10)","in":"query","name":"per_page","required":false,"schema":{"type":"integer"}},{"description":"Comma-separated speaker labels","in":"query","name":"speakers","required":false,"schema":{"type":"string"}},{"description":"Filter by a single collection UUID","in":"query","name":"collection_id","required":false,"schema":{"type":"string"}},{"description":"Filter by multiple collection UUIDs (OR semantics). Supports repeated query params and comma-separated values.","explode":true,"in":"query","name":"collection_ids","required":false,"schema":{"items":{"type":"string"},"type":"array"},"style":"form"},{"description":"Only include content published after this ISO 8601 timestamp","in":"query","name":"published_after","required":false,"schema":{"type":"string"}},{"description":"Only include content published before this ISO 8601 timestamp","in":"query","name":"published_before","required":false,"schema":{"type":"string"}},{"description":"Meilisearch sort expression, such as published_at:desc","in":"query","name":"sort","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}},"description":"Search results"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchErrorResponse"}}},"description":"Search failed"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiAuthErrorResponse"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"}},"summary":"Search public video content","tags":["Search"]}},"/api/v1/community/posts/{post_id}/comments":{"post":{"callbacks":{},"description":"Creates a new comment on a post. Requires the `X-Viewer-ID` header.\n\nTo create a reply to another comment, include `parent_id` in the request body.\nComments can be nested up to 2 levels deep.\n","operationId":"BoldAdminWeb.Api.CommunityController.create_comment","parameters":[{"description":"Post UUID","in":"path","name":"post_id","required":true,"schema":{"type":"string"}},{"description":"Viewer UUID (required)","in":"header","name":"X-Viewer-ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityCommentCreate"}}},"description":"Comment parameters","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityCommentResponse"}}},"description":"Comment created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid parameters"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Post not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors (e.g., max depth exceeded)"}},"summary":"Create a comment on a post","tags":["Community"]}},"/api/v1/viewers/lookup":{"get":{"callbacks":{},"description":"Finds a viewer by external_id or email. At least one query parameter must be provided.","operationId":"BoldAdminWeb.Api.ViewerController.lookup","parameters":[{"description":"External ID from course platform","in":"query","name":"external_id","required":false,"schema":{"type":"string"}},{"description":"Viewer email","in":"query","name":"email","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewerResponse"}}},"description":"Viewer found"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing query parameters"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers feature is not enabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer not found"}},"summary":"Look up a viewer","tags":["Viewers"]}},"/api/v1/videos":{"get":{"callbacks":{},"description":"Returns a paginated list of public videos for the current tenant.","operationId":"BoldAdminWeb.Api.VideoController.index","parameters":[{"description":"Page number","in":"query","name":"page","required":false,"schema":{"type":"integer"}},{"description":"Filter by tag","in":"query","name":"tag","required":false,"schema":{"type":"string"}},{"description":"Filter by a single collection UUID","in":"query","name":"collection_id","required":false,"schema":{"type":"string"}},{"description":"Filter by multiple collection UUIDs (OR semantics). Supports repeated query params and comma-separated values.","explode":true,"in":"query","name":"collection_ids","required":false,"schema":{"items":{"type":"string"},"type":"array"},"style":"form"},{"description":"Only return uncategorized videos","in":"query","name":"no_collection","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideosResponse"}}},"description":"Videos list"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"}},"summary":"List public videos","tags":["Videos"]},"post":{"callbacks":{},"description":"Accepts a video URL for ingestion. Returns immediately with a video ID. Poll GET /api/v1/videos/:id for processing status.","operationId":"BoldAdminWeb.Api.VideoController.create","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoIngestRequest"}}},"description":"Video ingestion request","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoIngestResponse"}}},"description":"Existing video (idempotent)"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoIngestResponse"}}},"description":"Video created"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Collection not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation error"}},"summary":"Create a video from URL","tags":["Videos"]}},"/api/v1/auth/sessions":{"get":{"callbacks":{},"operationId":"BoldAdminWeb.Api.Auth.SessionController.index","parameters":[{"description":"Tenant slug for the BOLD account, for example `hrtu`.","in":"header","name":"X-Bold-Tenant-Slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSessionsListResponse"}}},"description":"Active sessions"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid bearer JWT"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SelfManagementDisabledError"}}},"description":"Self-management disabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer not provisioned"}},"security":[{"upstream_jwt":[]}],"summary":"List active sessions for the authenticated viewer","tags":["Auth Sessions"]},"post":{"callbacks":{},"description":"Verifies the upstream IdP bearer JWT, resolves the viewer, runs device-limit enforcement per the tenant's auth_policies row. Captures a coarse session location from the connecting IP; trusted server-side integrators may pass `client_ip` to geo-locate the real end user instead of their server's egress IP.","operationId":"BoldAdminWeb.Api.Auth.SessionController.create","parameters":[{"description":"Tenant slug for the BOLD account, for example `hrtu`.","in":"header","name":"X-Bold-Tenant-Slug","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAuthSessionRequest"}}},"description":"Create-session payload","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSessionResponse"}}},"description":"Session created or bypassed"},"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSessionChallengeResponse"}}},"description":"Challenge required"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid bearer JWT"},"403":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/DeviceLimitError"},{"$ref":"#/components/schemas/Error"}]}}},"description":"Device limit exceeded or account inactive"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer not provisioned"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer email required for challenge delivery"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Challenge delivery unavailable"}},"security":[{"upstream_jwt":[]}],"summary":"Create a session for the authenticated viewer's device","tags":["Auth Sessions"]}},"/api/v1/ai/videos/{id}/chat":{"get":{"callbacks":{},"description":"Retrieve paginated conversation history for a specific video.","operationId":"BoldAdminWeb.Api.AiController.index","parameters":[{"description":"Video ID or slug","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Max results (default 20, max 100)","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"Pagination offset (default 0)","in":"query","name":"offset","required":false,"schema":{"type":"integer"}},{"description":"Viewer UUID or external ID","in":"query","name":"viewer","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoConversationListResponse"}}},"description":"Conversation list"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing viewer parameter"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"AI feature not available"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Video or viewer not found"}},"summary":"List video chat conversations","tags":["AI Chat"]},"post":{"callbacks":{},"description":"Ask questions about a specific video using its transcript.\n\nSet `stream=false` to receive a JSON response instead of SSE stream.\n\n**Streaming (default)**: Returns `text/event-stream` with events:\n- `message_start`: Initial event with conversation_id\n- `text_delta`: Incremental text chunks\n- `message_complete`: Final event with full content and metadata\n- `error`: Error event if something goes wrong\n","operationId":"BoldAdminWeb.Api.AiController.ask","parameters":[{"description":"Video ID or slug","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiChatRequest"}}},"description":"Chat request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiChatResponse"}}},"description":"Chat response (JSON when stream=false)"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid request"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"AI feature not available"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Video, transcript, or viewer not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Processing failed"}},"summary":"Chat about a video","tags":["AI Chat"]}},"/api/v1/ai/chat/{id}":{"delete":{"callbacks":{},"description":"Delete a library-scoped conversation and all associated messages.","operationId":"BoldAdminWeb.Api.AskStreamController.delete","parameters":[{"description":"Conversation ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"content":{"application/json":{}},"description":"Conversation deleted"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"AI feature not available"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Conversation not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Deletion failed"}},"summary":"Delete a library chat conversation","tags":["AI Chat"]},"get":{"callbacks":{},"description":"Retrieve the full message history for a conversation.","operationId":"BoldAdminWeb.Api.AskStreamController.show (2)","parameters":[{"description":"Conversation ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationResponse"}}},"description":"Conversation"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Forbidden — account inactive (subscription expired or no active plan) or required feature disabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Conversation not found"}},"summary":"Get conversation history","tags":["AI Chat"]},"post":{"callbacks":{},"description":"Continue an existing conversation with follow-up questions.\n\nThe AI maintains context from previous messages in the conversation.\nAnswer responses include public-safe retrieval metadata: `retrieval_mode`, `degraded`,\nand `degraded_reason`.\n\nAnswers are finalized the same way as \"Chat with your video library\": claims\ncarrying unknown citation markers are removed, exact cited video titles become\nMarkdown links, and the terminal `answer` event is authoritative over\nconcatenated `text_delta` chunks.\n","operationId":"BoldAdminWeb.Api.AskStreamController.continue_ask (2)","parameters":[{"description":"Conversation ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LibraryAiChatRequest"}}},"description":"Chat request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LibraryAiChatResponse"}}},"description":"Chat response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid request"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Forbidden — account inactive (subscription expired or no active plan) or required feature disabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Conversation not found"}},"summary":"Continue a library chat","tags":["AI Chat"]}},"/api/v1/playlists":{"get":{"callbacks":{},"description":"Returns all playlists for your account.","operationId":"BoldAdminWeb.Api.PlaylistController.index","parameters":[{"description":"Maximum number of playlists (1-1000, default: 50)","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"Offset for pagination (default: 0)","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaylistsResponse"}}},"description":"Playlists"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive — subscription expired or no active plan"}},"summary":"List playlists","tags":["Playlists"]}},"/api/v1/viewers":{"get":{"callbacks":{},"description":"Returns a paginated list of viewers for the current tenant.","operationId":"BoldAdminWeb.Api.ViewerController.index","parameters":[{"description":"Page number (default: 1)","example":1,"in":"query","name":"page","required":false,"schema":{"type":"integer"}},{"description":"Items per page (default: 20)","example":20,"in":"query","name":"page_size","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewersResponse"}}},"description":"Viewers list with pagination metadata"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers feature is not enabled"}},"summary":"List viewers (paginated)","tags":["Viewers"]},"post":{"callbacks":{},"description":"Creates a new viewer for the current tenant.","operationId":"BoldAdminWeb.Api.ViewerController.create","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewerCreate"}}},"description":"Viewer parameters","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewerResponse"}}},"description":"Viewer created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid parameters"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers feature is not enabled"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"summary":"Create a viewer","tags":["Viewers"]}},"/api/v1/ai/conversations":{"get":{"callbacks":{},"description":"Returns a unified list of all AI conversations (video and library) for a viewer, in reverse-chronological order.","operationId":"BoldAdminWeb.Api.ConversationsController.index","parameters":[{"description":"Viewer UUID or external ID","in":"query","name":"viewer","required":true,"schema":{"type":"string"}},{"description":"Results per page (max 100)","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"Offset for pagination","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnifiedConversationListResponse"}}},"description":"Conversation list"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing viewer parameter"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"AI not enabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer not found"}},"summary":"List all conversations for a viewer","tags":["AI Conversations"]}},"/api/v1/ai/chat":{"get":{"callbacks":{},"description":"Retrieve paginated conversation history for library-scoped AI chat.","operationId":"BoldAdminWeb.Api.AskStreamController.index","parameters":[{"description":"Max results (default 20, max 100)","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"Pagination offset (default 0)","in":"query","name":"offset","required":false,"schema":{"type":"integer"}},{"description":"Viewer UUID or external ID","in":"query","name":"viewer","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LibraryConversationListResponse"}}},"description":"Conversation list"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid profile_id"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Forbidden — account inactive (subscription expired or no active plan) or required feature disabled"}},"summary":"List library chat conversations","tags":["AI Chat"]},"post":{"callbacks":{},"description":"Ask questions across your entire video library.\n\nThe AI searches all transcripts, finds relevant content, and synthesizes an answer with citations.\nFactual name lookups use exact lexical recovery across profiles and transcripts. If factual\nretrieval is empty or weak, the response describes what the search could not find rather than asserting absence.\nUse `conversation_id` from the response to ask follow-up questions.\n\n**Streaming (default):** Returns SSE events:\n- `message_start`: Stream started\n- `conversation_created`: Conversation record created\n- `image_analysis`: Uploaded images analyzed\n- `progress`: Pipeline stage status (e.g. \"Searching your library…\")\n- `sources`: Search results found\n- `citation_map`: Stable citation map for streamed text\n- `text_delta`: Raw answer text chunk (pre-finalization, see below)\n- `answer`: Complete finalized answer with citations and public-safe retrieval metadata\n- `message_complete`: Stream finished (carries the same finalized `content`)\n\n**Answer finalization:** `text_delta` events stream raw model tokens. The final\nanswer text is then validated: any marker-delimited claim carrying an unknown\ncitation marker is removed (fail closed), and exact titles of cited videos are\nrewritten as Markdown links to the video's canonical portal URL. The terminal `answer` event\n(and `message_complete.content`) carry this finalized text — it is authoritative\nand is what gets persisted to the conversation. Clients MUST replace their\naccumulated `text_delta` buffer with `answer.content` on receipt; the finalized\ntext can differ from the concatenated deltas.\n\nAnswer responses include public-safe retrieval metadata: `retrieval_mode`, `degraded`,\nand `degraded_reason`. When retrieval recovers after Stage 1 exhaustion,\n`retrieval_mode` is `degraded` and `degraded_reason` is `stage1_exhausted`.\n\nSet `stream=false` for a single JSON response whose `content` is the same\nfinalized answer text, with the same retrieval metadata.\n","operationId":"BoldAdminWeb.Api.AskStreamController.ask","parameters":[{"description":"Set to \"true\" to bypass caching for this request. Requires allow_per_request_model to be enabled on the account.","in":"header","name":"X-Bold-Cache-Bypass","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LibraryAiChatRequest"}}},"description":"Chat request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LibraryAiChatResponse"}}},"description":"Chat response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid request"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"AI not enabled"}},"summary":"Chat with your video library","tags":["AI Chat"]}},"/api/v1/admin/viewers/{viewer_id}/sessions/{id}/revoke":{"post":{"callbacks":{},"operationId":"BoldAdminWeb.Api.Admin.ViewerSessionController.revoke","parameters":[{"description":"Viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}},{"description":"Session UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminRevokeAuthSessionRequest"}}},"description":"Optional reason","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminRevokeAuthSessionResponse"}}},"description":"Session revoked"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Session not found"}},"security":[{"admin_api_token":[]}],"summary":"Admin-revoke a single session","tags":["Admin Auth Sessions"]}},"/api/v1/viewers/{id}":{"delete":{"callbacks":{},"description":"Deletes an existing viewer.","operationId":"BoldAdminWeb.Api.ViewerController.delete","parameters":[{"description":"Viewer UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"content":{"application/json":{}},"description":"Viewer deleted"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers feature is not enabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer not found"}},"summary":"Delete a viewer","tags":["Viewers"]},"get":{"callbacks":{},"description":"Returns a single viewer by ID.","operationId":"BoldAdminWeb.Api.ViewerController.show","parameters":[{"description":"Viewer UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewerResponse"}}},"description":"Viewer details"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers feature is not enabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer not found"}},"summary":"Get a viewer","tags":["Viewers"]},"patch":{"callbacks":{},"description":"Updates an existing viewer.","operationId":"BoldAdminWeb.Api.ViewerController.update (2)","parameters":[{"description":"Viewer UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewerUpdate"}}},"description":"Viewer parameters","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewerResponse"}}},"description":"Viewer updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid parameters"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers feature is not enabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"summary":"Update a viewer","tags":["Viewers"]},"put":{"callbacks":{},"description":"Updates an existing viewer.","operationId":"BoldAdminWeb.Api.ViewerController.update","parameters":[{"description":"Viewer UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewerUpdate"}}},"description":"Viewer parameters","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViewerResponse"}}},"description":"Viewer updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid parameters"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Account inactive, or the viewers feature is not enabled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Viewer not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"summary":"Update a viewer","tags":["Viewers"]}},"/api/v1/admin/auth/policy":{"get":{"callbacks":{},"description":"Returns max_devices_per_user, enforcement_mode/phase, session_ttl_days, allow_member_self_manage, admin_override_per_user, and auth challenge email branding fields. JWT-verification config (jwks_url, jwt_issuer, jwt_audience) is deliberately excluded.","operationId":"BoldAdminWeb.Api.Admin.PolicyController.show","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminAuthPolicyResponse"}}},"description":"Policy"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"}},"security":[{"admin_api_token":[]}],"summary":"Get the tenant's user-tunable auth policy","tags":["Admin Auth Policy"]},"put":{"callbacks":{},"description":"Casts any provided user-tunable fields and preserves omitted values. Auth challenge email branding may be configured here, but arbitrary From-domain configuration is not supported. JWKS/issuer/audience are silently ignored if present in the request body — they are infra config edited via Mission Control, not over the API.","operationId":"BoldAdminWeb.Api.Admin.PolicyController.update","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminAuthPolicyUpdateRequest"}}},"description":"User-tunable policy fields","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminAuthPolicyResponse"}}},"description":"Updated policy"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation failed"}},"security":[{"admin_api_token":[]}],"summary":"Update the tenant's user-tunable auth policy","tags":["Admin Auth Policy"]}}},"security":[{"api_token":[]}],"servers":[{"description":"Production API","url":"https://app.boldvideo.io","variables":{}}],"tags":[]}