{"components":{"responses":{},"schemas":{"AdminCommunityPostCreate":{"description":"Parameters for importing a community post","example":{"post":{"author":{"external_id":"outseta:AbC123","name":"Jane Doe"},"category":"announcements","content":"Kicking off week one — introduce yourself!","created_at":"2024-03-01T12:00:00Z","external_ref":"kajabi:post:9182","mentions":["outseta:XyZ789"],"reactions":{"count":42}}},"properties":{"post":{"description":"Post parameters","properties":{"author":{"$ref":"#/components/schemas/CommunityImportAuthor"},"category":{"description":"Post category (optional, defaults to 'general')","enum":["announcements","q_and_a","general"],"example":"announcements","type":"string"},"content":{"description":"Post content (required)","example":"Kicking off week one — introduce yourself!","type":"string"},"created_at":{"description":"Original posting time, ISO-8601. Truncated to seconds. A time in the future is rejected (422).","example":"2024-03-01T12:00:00Z","format":"date-time","type":"string"},"external_ref":{"description":"Idempotency key from the source system. Replaying it returns 200 with the stored post.","example":"kajabi:post:9182","type":"string"},"mentions":{"$ref":"#/components/schemas/MentionsParam"},"reactions":{"$ref":"#/components/schemas/CommunityImportReactions"}},"required":["content","author"],"type":"object"}},"required":["post"],"title":"AdminCommunityPostCreate","type":"object"},"Mention":{"description":"A record that the current viewer was mentioned in a post or a comment","properties":{"author":{"$ref":"#/components/schemas/CommunityAuthor"},"comment_id":{"description":"The comment the mention lives in, or null when the mention was in the post itself","format":"uuid","nullable":true,"type":"string"},"created_at":{"description":"When the mention was recorded","format":"date-time","type":"string"},"excerpt":{"description":"First non-empty line of the mentioning content as plain text — markup stripped, common entities decoded — ellipsized at 140 characters. Safe to render as-is.","example":"Hey @jane — could you take a look at this?","nullable":true,"type":"string"},"id":{"description":"Mention UUID","format":"uuid","type":"string"},"post_id":{"description":"The post the mention lives in. Always present — for a mention in a comment this is the comment's post, so every row can be deep-linked.","format":"uuid","type":"string"},"read_at":{"description":"When the mention was marked read, or null when unread","format":"date-time","nullable":true,"type":"string"}},"required":["id","post_id","author","created_at"],"title":"Mention","type":"object"},"NotificationChannels":{"description":"Per-viewer notification channel switches. Both default to true.","properties":{"email":{"description":"Whether the viewer receives notifications by email.","example":true,"type":"boolean"},"push":{"description":"Whether the viewer receives notifications by push.","example":true,"type":"boolean"}},"required":["email","push"],"title":"NotificationChannels","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"},"CollectionResponse":{"description":"Response containing a single collection","properties":{"data":{"$ref":"#/components/schemas/Collection"}},"required":["data"],"title":"CollectionResponse","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"},"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"},"CommunityImportReactions":{"description":"Historical engagement, seeded as a count only — reactor identities are not\nimported, so an imported row reports a count with an empty `reacted_by`.\nReactions added natively afterwards still increment correctly.\n","properties":{"count":{"description":"Reaction count","example":42,"minimum":0,"type":"integer"}},"required":["count"],"title":"CommunityImportReactions","type":"object"},"MentionsResponse":{"description":"Mentions of the current viewer, newest first","properties":{"data":{"description":"List of mentions","items":{"$ref":"#/components/schemas/Mention"},"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 mentions","example":7,"type":"integer"},"total_pages":{"description":"Total number of pages","example":1,"type":"integer"}},"required":["page","page_size","total_entries","total_pages"],"type":"object"}},"required":["data","meta"],"title":"MentionsResponse","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"},"RevokeAuthSessionResponse":{"properties":{"ok":{"type":"boolean"},"session_id":{"format":"uuid","type":"string"}},"required":["ok","session_id"],"title":"RevokeAuthSessionResponse","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"},"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"},"AdminRevokeAuthSessionRequest":{"properties":{"reason":{"nullable":true,"type":"string"}},"title":"AdminRevokeAuthSessionRequest","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"},"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"},"RegisterDeviceResponse":{"example":{"ok":true,"provider":"expo","session_id":"0b2f2e3f-7f1e-4a5f-9f1a-6c1f2d3e4a5b"},"properties":{"ok":{"type":"boolean"},"provider":{"enum":["expo","fcm","apns"],"type":"string"},"session_id":{"format":"uuid","type":"string"}},"required":["ok","session_id","provider"],"title":"RegisterDeviceResponse","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"},"CommunityImportAuthor":{"description":"Author attribution by the customer's own member id. Bold looks up a viewer\nwith this `external_id` and creates one if it has never seen them. An\nexisting viewer is **never** modified, so the remaining fields only take\neffect on creation.\n","properties":{"avatar_url":{"format":"uri","nullable":true,"type":"string"},"email":{"format":"email","nullable":true,"type":"string"},"external_id":{"description":"Customer member id (e.g. the Outseta uid)","example":"outseta:AbC123","type":"string"},"name":{"description":"Name for a newly created viewer; falls back to external_id","example":"Jane Doe","nullable":true,"type":"string"}},"required":["external_id"],"title":"CommunityImportAuthor","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"},"MentionsReadResponse":{"description":"How many mentions were flipped, and the resulting badge count","example":{"data":{"marked_read":2,"unread_count":0}},"properties":{"data":{"properties":{"marked_read":{"description":"Mentions that moved from unread to read in this call","example":2,"type":"integer"},"unread_count":{"description":"Unread mentions remaining after the call","example":0,"type":"integer"}},"required":["marked_read","unread_count"],"type":"object"}},"required":["data"],"title":"MentionsReadResponse","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"},"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"},"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"},"VerifyAuthChallengeRequest":{"properties":{"code":{"description":"Six-digit verification code from the latest challenge email.","pattern":"^\\d{6}$","type":"string"}},"required":["code"],"title":"VerifyAuthChallengeRequest","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"},"AdminSessionManagementExemptionRequest":{"properties":{"exempt":{"description":"Whether this viewer is exempt from session-management enforcement.","type":"boolean"}},"required":["exempt"],"title":"AdminSessionManagementExemptionRequest","type":"object"},"Video":{"description":"A video resource","properties":{"chapters":{"description":"AI-generated chapter data","items":{"type":"object"},"nullable":true,"type":"array"},"cta":{"description":"Effective call-to-action for this video","nullable":true,"type":"object"},"thumbnail":{"description":"Thumbnail URL","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"},"type":{"description":"Resource type","example":"video","type":"string"},"teaser":{"description":"Short teaser text","nullable":true,"type":"string"},"playback_speed":{"description":"Default playback speed","nullable":true,"type":"number"},"duration":{"description":"Duration in seconds","example":120,"type":"integer"},"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"},"captions_lang":{"description":"Caption language code","nullable":true,"type":"string"},"source_provider":{"description":"Original provider key (mirrors imported_from). Included only when playback_mode is \"source\".","example":"youtube","nullable":true,"type":"string"},"captions":{"description":"Caption file URL","nullable":true,"type":"string"},"subtitles":{"description":"Subtitle track metadata","nullable":true,"type":"object"},"chapters_url":{"description":"Signed URL for chapter data","nullable":true,"type":"string"},"internal_id":{"description":"Internal UUID","example":"550e8400-e29b-41d4-a716-446655440000","format":"uuid","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"},"updated_at":{"description":"Last update timestamp","format":"date-time","type":"string"},"progress":{"description":"Viewer's watch progress (only present when viewer_id is provided)","nullable":true,"oneOf":[{"$ref":"#/components/schemas/VideoProgress"}]},"imported_from":{"description":"Original import source when available","nullable":true,"type":"string"},"description":{"description":"Full video description","nullable":true,"type":"string"},"id":{"description":"Video ID (hashid)","example":"abc123","type":"string"},"legacy_video_url":{"description":"Legacy source video URL when available","nullable":true,"type":"string"},"playback_id":{"description":"Mux playback ID","nullable":true,"type":"string"},"tags":{"description":"Tags assigned to this video","items":{"type":"object"},"type":"array"},"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"},"transcript":{"description":"Transcript track URLs keyed by format","type":"object"},"meta_data":{"description":"Embeddable metadata for title, description, and image","type":"object"},"download_urls":{"description":"Available download URLs keyed by quality or asset type","nullable":true,"type":"object"},"stream_url":{"description":"Playable stream URL","nullable":true,"type":"string"},"slug":{"description":"URL-friendly identifier (optional, unique)","example":"introduction-to-bold","nullable":true,"type":"string"},"captions_label":{"description":"Caption language label","nullable":true,"type":"string"},"published_at":{"description":"Publication timestamp","format":"date-time","nullable":true,"type":"string"},"title":{"description":"Video title","example":"Introduction to BOLD","type":"string"},"created_at":{"description":"Creation timestamp","format":"date-time","type":"string"}},"required":["id","title"],"title":"Video","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"},"SessionManagementRevokeSessionResponse":{"properties":{"ok":{"type":"boolean"},"session_id":{"format":"uuid","type":"string"}},"required":["ok","session_id"],"title":"SessionManagementRevokeSessionResponse","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"},"AdminDeviceLimitRequest":{"properties":{"limit":{"minimum":1,"nullable":true,"type":"integer"}},"title":"AdminDeviceLimitRequest","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"},"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"},"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"},"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"},"SessionManagementViewerSessionsResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/SessionManagementSessionWithRevocation"},"type":"array"},"viewer":{"$ref":"#/components/schemas/SessionManagementViewerState"}},"required":["viewer","data"],"title":"SessionManagementViewerSessionsResponse","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"},"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"},"MentionsReadRequest":{"description":"Which mentions to mark read. Send `ids` to mark specific mentions, or `all: true`\nto mark every unread mention of the current viewer. Ids belonging to another\nviewer are ignored.\n","example":{"ids":["6b3d0d3e-2b0a-4c66-9a2f-6c1e0f4a7b21"]},"properties":{"all":{"description":"Set true to mark every unread mention read","example":true,"type":"boolean"},"ids":{"description":"Mention UUIDs to mark read","items":{"format":"uuid","type":"string"},"type":"array"}},"title":"MentionsReadRequest","type":"object"},"ConversationPagination":{"properties":{"has_more":{"type":"boolean"},"limit":{"type":"integer"},"offset":{"type":"integer"}},"required":["limit","offset","has_more"],"title":"ConversationPagination","type":"object"},"SessionManagementRevokeSessionRequest":{"properties":{"reason":{"maxLength":255,"nullable":true,"type":"string"}},"title":"SessionManagementRevokeSessionRequest","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"},"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"},"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"},"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"},"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"},"CommunityPostCreate":{"description":"Parameters for creating a community post","example":{"post":{"category":"announcements","content":"Welcome to our community!","mentions":["outseta:AbC123"]}},"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"},"mentions":{"$ref":"#/components/schemas/MentionsParam"}},"required":["content"],"type":"object"}},"required":["post"],"title":"CommunityPostCreate","type":"object"},"SessionManagementRevokeAllResponse":{"properties":{"ok":{"type":"boolean"},"revoked_count":{"type":"integer"}},"required":["ok","revoked_count"],"title":"SessionManagementRevokeAllResponse","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"},"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"},"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"},"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"},"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"},"NotificationPreferencesResponse":{"description":"A viewer's notification channel preferences.","properties":{"channels":{"$ref":"#/components/schemas/NotificationChannels"}},"required":["channels"],"title":"NotificationPreferencesResponse","type":"object"},"ViewerResponse":{"description":"Response containing a single viewer","properties":{"data":{"$ref":"#/components/schemas/Viewer"}},"required":["data"],"title":"ViewerResponse","type":"object"},"AdminRevokeAllResponse":{"properties":{"ok":{"type":"boolean"},"revoked_count":{"type":"integer"}},"required":["ok","revoked_count"],"title":"AdminRevokeAllResponse","type":"object"},"SelfManagementDisabledError":{"properties":{"error":{"enum":["self_management_disabled"],"type":"string"}},"required":["error"],"title":"SelfManagementDisabledError","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"},"AdminRevokeAuthSessionResponse":{"properties":{"ok":{"type":"boolean"},"session_id":{"format":"uuid","type":"string"}},"required":["ok","session_id"],"title":"AdminRevokeAuthSessionResponse","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"},"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"},"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"},"LibraryConversationListResponse":{"properties":{"conversations":{"items":{"$ref":"#/components/schemas/ConversationSummary"},"type":"array"},"pagination":{"$ref":"#/components/schemas/ConversationPagination"}},"required":["conversations","pagination"],"title":"LibraryConversationListResponse","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"},"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"},"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"},"ImageUploadRequest":{"description":"Multipart image upload","properties":{"file":{"description":"Image file bytes. Sniffed by magic bytes against jpeg/png/gif/webp/avif (the client-declared content type is never trusted); SVG is rejected. Max 5 MB.","format":"binary","type":"string"}},"required":["file"],"title":"ImageUploadRequest","type":"object"},"ConversationStartersResponse":{"description":"Response containing the resolved conversation-starter pool","properties":{"data":{"description":"List of resolved conversation starters (order is deterministic but not meaningful)","items":{"$ref":"#/components/schemas/ConversationStarter"},"type":"array"}},"required":["data"],"title":"ConversationStartersResponse","type":"object"},"SessionManagementViewerStateResponse":{"properties":{"data":{"$ref":"#/components/schemas/SessionManagementViewerState"}},"required":["data"],"title":"SessionManagementViewerStateResponse","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"},"NotificationPreferencesUpdate":{"description":"Notification channels to update. Provide one or both.","properties":{"channels":{"description":"Channel switches to update.","properties":{"email":{"description":"Set false to stop email notifications; true to resume.","example":false,"type":"boolean"},"push":{"description":"Set false to stop push notifications; true to resume.","example":true,"type":"boolean"}},"type":"object"}},"required":["channels"],"title":"NotificationPreferencesUpdate","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"},"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"},"PlaylistsResponse":{"description":"List of playlists","properties":{"data":{"description":"Playlists","items":{"$ref":"#/components/schemas/Playlist"},"type":"array"}},"required":["data"],"title":"PlaylistsResponse","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"},"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"},"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"},"CommunityCommentResponse":{"description":"Response containing a single comment","properties":{"data":{"$ref":"#/components/schemas/CommunityComment"},"mentions":{"$ref":"#/components/schemas/MentionsSkipped"}},"required":["data"],"title":"CommunityCommentResponse","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"},"VideoResponse":{"description":"Response containing a single video","properties":{"data":{"$ref":"#/components/schemas/Video"}},"required":["data"],"title":"VideoResponse","type":"object"},"RevokeOthersResponse":{"properties":{"ok":{"type":"boolean"},"revoked_count":{"type":"integer"}},"required":["ok","revoked_count"],"title":"RevokeOthersResponse","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"},"VerifyAuthSessionResponse":{"properties":{"reason":{"enum":["session_not_found","session_revoked","session_expired"],"nullable":true,"type":"string"},"valid":{"type":"boolean"}},"required":["valid"],"title":"VerifyAuthSessionResponse","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"},"SessionManagementViewerResolveResponse":{"properties":{"data":{"$ref":"#/components/schemas/SessionManagementViewerState"}},"required":["data"],"title":"SessionManagementViewerResolveResponse","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"},"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"},"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"},"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"},"AdminCommunityPostUpdate":{"description":"Parameters for repairing a post's content. Only `content` is accepted —\nauthor, category, counters, and `external_ref` cannot be changed through\nthis route, even if present in the request body.\n","example":{"post":{"content":"Kicking off week one — introduce yourself! (fixed image links)"}},"properties":{"post":{"description":"Post parameters","properties":{"content":{"description":"Corrected post content (required)","example":"Kicking off week one — introduce yourself! (fixed image links)","maxLength":10000,"minLength":1,"type":"string"}},"required":["content"],"type":"object"}},"required":["post"],"title":"AdminCommunityPostUpdate","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"},"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"},"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"},"ImageResponse":{"description":"Response after uploading an image","properties":{"data":{"description":"Uploaded image details","properties":{"content_type":{"description":"Sniffed content type (never the client-declared one)","example":"image/jpeg","type":"string"},"key":{"description":"Storage key","example":"uploads/bt_demo/images/abc123.jpg","type":"string"},"size":{"description":"File size in bytes","example":102400,"type":"integer"},"url":{"description":"Public URL of the stored image","example":"https://uploads.eu1.boldvideo.io/uploads/bt_demo/images/abc123.jpg","format":"uri","type":"string"}},"required":["url","key","content_type","size"],"type":"object"}},"required":["data"],"title":"ImageResponse","type":"object"},"RegisterDeviceRequest":{"description":"Push credentials for the device that owns this session. Registering again with a new token replaces the old one in place.","example":{"provider":"expo","token":"ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]"},"properties":{"provider":{"description":"Push provider for this token. Only `expo` is delivered today; `fcm` and `apns` are accepted and stored for clients that bypass Expo's relay.","enum":["expo","fcm","apns"],"type":"string"},"token":{"description":"The device push token. For `expo` this is the `ExponentPushToken[...]` string returned by `getExpoPushTokenAsync`; any other shape is rejected.","maxLength":255,"type":"string"}},"required":["provider","token"],"title":"RegisterDeviceRequest","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"},"AdminViewerSessionsResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/AdminSessionWithRevocation"},"type":"array"}},"required":["data"],"title":"AdminViewerSessionsResponse","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"},"ViewerMemoryResponse":{"description":"Response containing a viewer's memory document.","properties":{"data":{"$ref":"#/components/schemas/ViewerMemory"}},"required":["data"],"title":"ViewerMemoryResponse","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"},"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"},"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"},"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"},"CommunityPostResponse":{"description":"Response containing a single community post with comments","properties":{"data":{"$ref":"#/components/schemas/CommunityPost"},"mentions":{"$ref":"#/components/schemas/MentionsSkipped"}},"required":["data"],"title":"CommunityPostResponse","type":"object"},"SessionManagementExemptionRequest":{"properties":{"exempt":{"description":"Whether this viewer is exempt from session-management enforcement.","type":"boolean"}},"required":["exempt"],"title":"SessionManagementExemptionRequest","type":"object"},"CommunityCommentCreate":{"description":"Parameters for creating a comment","example":{"comment":{"content":"Great post! Thanks for sharing.","mentions":["outseta:AbC123"]}},"properties":{"comment":{"description":"Comment parameters","properties":{"content":{"description":"Comment content (required)","example":"Great post! Thanks for sharing.","type":"string"},"mentions":{"$ref":"#/components/schemas/MentionsParam"},"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"},"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"},"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"},"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"},"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"},"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"},"AdminCommunityCommentCreate":{"description":"Parameters for importing a comment","example":{"comment":{"author":{"external_id":"outseta:XyZ789"},"content":"Welcome aboard!","created_at":"2024-03-02T09:30:00Z","external_ref":"kajabi:comment:4471"}},"properties":{"comment":{"description":"Comment parameters","properties":{"author":{"$ref":"#/components/schemas/CommunityImportAuthor"},"content":{"description":"Comment content (required)","example":"Welcome aboard!","type":"string"},"created_at":{"description":"Original posting time, ISO-8601. Truncated to seconds. A time in the future is rejected (422).","example":"2024-03-02T09:30:00Z","format":"date-time","type":"string"},"external_ref":{"description":"Idempotency key from the source system. Replaying it returns 200 with the stored comment.","example":"kajabi:comment:4471","type":"string"},"mentions":{"$ref":"#/components/schemas/MentionsParam"},"parent_id":{"description":"Parent comment UUID for replies (optional, max depth 2)","format":"uuid","nullable":true,"type":"string"},"reactions":{"$ref":"#/components/schemas/CommunityImportReactions"}},"required":["content","author"],"type":"object"}},"required":["comment"],"title":"AdminCommunityCommentCreate","type":"object"},"PlaylistResponse":{"description":"Single playlist with videos","properties":{"data":{"$ref":"#/components/schemas/Playlist"}},"required":["data"],"title":"PlaylistResponse","type":"object"},"ConversationStarter":{"description":"A conversation starter resolved from a collection or the account-level list","properties":{"collection_id":{"description":"The collection this starter belongs to, or null for account-sourced starters","format":"uuid","nullable":true,"type":"string"},"source":{"description":"Whether this starter came from the collection's own list or the account fallback","enum":["collection","account"],"type":"string"},"text":{"description":"The starter text","example":"What labs before starting a GLP-1?","type":"string"}},"required":["text","source","collection_id"],"title":"ConversationStarter","type":"object"},"AuthSessionsListResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/ActiveSessionSummary"},"type":"array"}},"required":["data"],"title":"AuthSessionsListResponse","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"},"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"},"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"},"MentionsUnreadCountResponse":{"description":"Number of unread mentions for the current viewer","example":{"data":{"count":3}},"properties":{"data":{"properties":{"count":{"description":"Unread mention count","example":3,"type":"integer"}},"required":["count"],"type":"object"}},"required":["data"],"title":"MentionsUnreadCountResponse","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"},"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"},"MentionsSkipped":{"description":"Present only when the request carried a `mentions` array. `skipped` echoes back the\nsupplied ids that were **not** recorded — blank or malformed ids, ids beyond the\n25-id cap, and anything Bold could not resolve. A self-mention is dropped silently\nand duplicates collapse, so neither appears here. A bad id never fails the write.\n","properties":{"skipped":{"description":"Supplied mention ids that were not recorded","example":["","outseta:unknown-shape"],"items":{"type":"string"},"type":"array"}},"required":["skipped"],"title":"MentionsSkipped","type":"object"},"MentionsParam":{"description":"Mentioned members, given as **customer external ids** (e.g. Outseta uids) — never Bold\nviewer ids, because a mentioned member may not have a viewer yet. Bold resolves each id\nto a viewer, creating one it has never seen, and stores the mention rows in the same\ntransaction as the write.\n\nCapped at 25 ids. Duplicates collapse, a self-mention is dropped, and any id Bold could\nnot record is returned in `mentions.skipped` — a bad id never fails the write.\n\nBold does not parse `content`: it neither looks for `@handles` nor checks that a passed\nmention appears in the text.\n","example":["outseta:AbC123","outseta:XyZ789"],"items":{"type":"string"},"maxItems":25,"title":"MentionsParam","type":"array"},"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"}},"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/community/mentions/unread-count":{"get":{"callbacks":{},"description":"Returns the number of mentions of the `X-Viewer-ID` viewer that have not been marked read. Intended for a badge, so it is a single indexed count.","operationId":"BoldAdminWeb.Api.CommunityController.unread_count","parameters":[{"description":"Viewer UUID (required)","in":"header","name":"X-Viewer-ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MentionsUnreadCountResponse"}}},"description":"Unread mention count"},"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"}},"summary":"Count unread mentions of the current viewer","tags":["Community"]}},"/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/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/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/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/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":{"get":{"callbacks":{},"description":"Returns a paginated list of public videos for the current tenant.","operationId":"BoldAdminWeb.Api.VideoController.index (2)","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/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/auth/sessions/{id}/device":{"post":{"callbacks":{},"description":"Attaches a push token to one of the authenticated viewer's active sessions. The token lives on the session, so revoking that session stops its pushes; re-registering the same device updates the token in place. Requires session management to be enabled for the account and its policy to be out of the `off` phase — without sessions there is nothing to attach a token to.","operationId":"BoldAdminWeb.Api.Auth.SessionDeviceController.create","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"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDeviceRequest"}}},"description":"Device registration payload","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDeviceResponse"}}},"description":"Device registered"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or malformed provider/token"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid bearer JWT"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Session management not available for this account"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Session not found, revoked, or not owned by the viewer"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"upstream_jwt":[]}],"summary":"Register a device push token on a session","tags":["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/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/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 (4)","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/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/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/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/images":{"post":{"callbacks":{},"description":"Uploads an image and stores it in Bold's public bucket, returning a stable\n`uploads.eu1.boldvideo.io` URL.\n\nThe file is sniffed by magic bytes against an allowlist (jpeg, png, gif, webp,\navif) — the client-declared content type is never trusted. SVG is rejected.\nMaximum file size is 5 MB.\n\nRequires the `X-Viewer-ID` header to identify the uploading viewer.\n","operationId":"BoldAdminWeb.Api.ImageController.create","parameters":[{"description":"Viewer UUID (required)","in":"header","name":"X-Viewer-ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/ImageUploadRequest"}}},"description":"Image file","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageResponse"}}},"description":"Image uploaded"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unsupported media type or missing file"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unauthorized - X-Viewer-ID header required or viewer not found"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"File too large (max 5 MB)"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Upload failed"}},"summary":"Upload an image","tags":["Images"]}},"/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/community/mentions":{"get":{"callbacks":{},"description":"Returns the mentions of the viewer identified by the `X-Viewer-ID` header, newest\nfirst. Each entry carries everything an inbox row needs — the post (and comment, when\nthe mention was in a comment), the author, a short excerpt of the mentioning content,\nand `read_at`.\n\n`post_id` is always present, including for mentions in comments, so every row can be\ndeep-linked. `excerpt` is plain text: markup is stripped (a mention contributes its\nvisible `@Name`) and common entities are decoded, so it can be rendered as-is.\n","operationId":"BoldAdminWeb.Api.CommunityController.mentions","parameters":[{"description":"Viewer UUID (required)","in":"header","name":"X-Viewer-ID","required":true,"schema":{"type":"string"}},{"description":"Page number (default: 1)","in":"query","name":"page","required":false,"schema":{"type":"integer"}},{"description":"Items per page (default: 20, max: 100)","in":"query","name":"page_size","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MentionsResponse"}}},"description":"Mentions list"},"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"}},"summary":"List mentions of the current viewer","tags":["Community"]}},"/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/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}/notification-preferences":{"get":{"callbacks":{},"description":"Returns the viewer's notification channel switches as `{\"channels\": {\"email\": bool, \"push\": bool}}`. Both default to true. Requires the `viewers` feature.","operationId":"BoldAdminWeb.Api.ViewerNotificationPreferencesController.show","parameters":[{"description":"Viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPreferencesResponse"}}},"description":"Notification preferences"},"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's notification preferences","tags":["Viewers"]},"patch":{"callbacks":{},"description":"Updates one or both notification channels via `{\"channels\": {\"email\": bool, \"push\": bool}}`. An absent channel is left unchanged. Requires the `viewers` feature.","operationId":"BoldAdminWeb.Api.ViewerNotificationPreferencesController.update","parameters":[{"description":"Viewer UUID","in":"path","name":"viewer_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPreferencesUpdate"}}},"description":"Notification preferences","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPreferencesResponse"}}},"description":"Updated notification preferences"},"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's notification preferences","tags":["Viewers"]}},"/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}/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/{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/community/posts/{post_id}/comments":{"post":{"callbacks":{},"description":"Creates a comment (or, with `parent_id`, a reply) on an existing post using\nthe same import parameters as post creation. Comments nest up to 2 levels.\n\nThe post's `comments_count` is incremented in the same transaction, exactly\nas for a natively-created comment, and so are any `mentions`.\n\n**Mentions recorded here never send a notification**, exactly as on post\ncreation.\n","operationId":"BoldAdminWeb.Api.Admin.CommunityController.create_comment","parameters":[{"description":"Post UUID","in":"path","name":"post_id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminCommunityCommentCreate"}}},"description":"Comment import parameters","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityCommentResponse"}}},"description":"Comment already imported under this external_ref"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityCommentResponse"}}},"description":"Comment created"},"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":"Post or parent comment not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Validation errors"}},"security":[{"admin_api_token":[]}],"summary":"Create a comment with import parameters","tags":["Admin Community"]}},"/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/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/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/playlists":{"get":{"callbacks":{},"description":"Returns all playlists for your account.","operationId":"BoldAdminWeb.Api.PlaylistController.index (2)","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/community/mentions/read":{"post":{"callbacks":{},"description":"Marks mentions of the `X-Viewer-ID` viewer as read. Send `{\"ids\": [...]}` to mark\nspecific mentions or `{\"all\": true}` to mark every unread one.\n\nOnly the calling viewer's own mentions are touched, so an id belonging to someone else\nis a no-op. Already-read mentions keep their original `read_at`.\n","operationId":"BoldAdminWeb.Api.CommunityController.mark_read","parameters":[{"description":"Viewer UUID (required)","in":"header","name":"X-Viewer-ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MentionsReadRequest"}}},"description":"Mentions to mark read","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MentionsReadResponse"}}},"description":"Mentions marked read"},"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"}},"summary":"Mark mentions read","tags":["Community"]}},"/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/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/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/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"]}},"/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/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/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/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/conversation-starters":{"get":{"callbacks":{},"description":"Resolves the conversation-starter pool for a set of collections, falling back to\nthe account-level list for any collection with none of its own. Results are\nunioned and deduped on exact text across the requested collections; each entry\nis tagged with its `source` (`collection` or `account`) and `collection_id`\n(`null` for account-sourced starters).\n\nWith no `collection_ids`, returns the account-level starters only.\n\nUnknown or non-UUID ids are silently ignored — same lenient posture as other\ncollection-scoped endpoints.\n","operationId":"BoldAdminWeb.Api.ConversationStartersController.index","parameters":[{"description":"Collection UUIDs — repeat the param (`?collection_ids=a&collection_ids=b`) or pass a comma-separated string. Unknown ids are ignored.","explode":true,"in":"query","name":"collection_ids","required":false,"schema":{"items":{"type":"string"},"type":"array"},"style":"form"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationStartersResponse"}}},"description":"Resolved conversation starters"},"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":"Resolve conversation starters","tags":["Collections"]}},"/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/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/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/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/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/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 (2)","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/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/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/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/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/admin/images":{"post":{"callbacks":{},"description":"Uploads an image and stores it in Bold's public bucket, returning a stable\n`uploads.eu1.boldvideo.io` URL.\n\nThe file is sniffed by magic bytes against an allowlist (jpeg, png, gif, webp,\navif) — the client-declared content type is never trusted. SVG is rejected.\nMaximum file size is 5 MB.\n","operationId":"BoldAdminWeb.Api.Admin.ImageController.create","parameters":[],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/ImageUploadRequest"}}},"description":"Image file","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageResponse"}}},"description":"Image uploaded"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Unsupported media type or missing file"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Unauthorized"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"File too large (max 5 MB)"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Upload failed"}},"security":[{"admin_api_token":[]}],"summary":"Upload an image","tags":["Admin Images"]}},"/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\nPass `mentions` — an array of **customer external ids**, never Bold viewer ids — to\nrecord mentions. Bold resolves each id to a viewer, creating one it has never seen,\nand stores the mention rows in the same transaction as the post. The array is capped\nat 25 ids, duplicates collapse, and a self-mention is dropped. Any id Bold could not\nrecord comes back in `mentions.skipped`; a bad id never fails the 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/admin/community/posts":{"post":{"callbacks":{},"description":"Creates a community post on behalf of a member, preserving the original\nposting time and engagement count. Intended for migrating an existing\ncommunity into Bold.\n\nThe author is resolved by `author.external_id`: an existing viewer with that\nid is reused untouched, otherwise one is created from the supplied name,\nemail and avatar (falling back to the external id for the name).\n\nSupplying `external_ref` makes the call idempotent — replaying the same\nreference returns **200** with the previously stored post instead of\ncreating a second one.\n\n`mentions` takes customer external ids and behaves exactly as on the public\nroute: resolved (or created), capped at 25, deduped, self-mention dropped,\nwith anything unrecorded returned in `mentions.skipped`.\n\n**Mentions recorded here never send a notification.** The rows are written,\nso the mention inbox and unread count are correct the first time the member\nopens the app, but no email or push is delivered — importing a backlog must\nnot notify a member about a two-year-old conversation.\n","operationId":"BoldAdminWeb.Api.Admin.CommunityController.create_post","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminCommunityPostCreate"}}},"description":"Post import parameters","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPostResponse"}}},"description":"Post already imported under this external_ref"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPostResponse"}}},"description":"Post created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Invalid request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Validation errors"}},"security":[{"admin_api_token":[]}],"summary":"Create a community post with import parameters","tags":["Admin Community"]}},"/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/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/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/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","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/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/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/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\nPass `mentions` — an array of **customer external ids** — to record mentions, with the\nsame rules as post creation: capped at 25, deduped, self-mentions dropped, and any id\nBold could not record returned in `mentions.skipped`.\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/admin/community/posts/{id}":{"patch":{"callbacks":{},"description":"Updates only a post's `content`. Every other field — author, category,\ncounters, `external_ref` — is untouched, even if the request body carries\nit.\n\nThis is a **different verb** from `external_ref` replay: replaying a\n`POST` with a known `external_ref` stays a pure read of the stored row.\nThis `PATCH` is the only way to correct a botched import's content\nwithout deleting and re-importing the post.\n\nNever notifies, same as post/comment creation on this surface.\n","operationId":"BoldAdminWeb.Api.Admin.CommunityController.update_post","parameters":[{"description":"Post UUID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminCommunityPostUpdate"}}},"description":"Post content","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPostResponse"}}},"description":"Post 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":"Post not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErrorResponse"}}},"description":"Validation errors"}},"security":[{"admin_api_token":[]}],"summary":"Update a community post's content","tags":["Admin Community"]}}},"security":[{"api_token":[]}],"servers":[{"description":"Production API","url":"https://app.boldvideo.io","variables":{}}],"tags":[]}