{"openapi":"3.1.0","info":{"title":"Ask Channel AI API","version":"v1","description":"Public API documentation for Ask Channel AI. API calls require a full Bearer token from an account with a subscription. Internal and debug endpoints are excluded."},"servers":[{"url":"/api/v1"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Use the full askchannelai_ token created on /api-docs/tokens."}}},"paths":{"/channels/discover":{"get":{"operationId":"get_channels_discover","summary":"Find imported channels and saved-transcript coverage.","tags":["channels"],"x-auth":"user","x-side-effects":"read","x-visibility":"external-beta","x-legacy-path":"/api/channels/discover","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string"},"description":"Channel title, @handle, ID or YouTube channel URL.","example":"apple"},{"name":"limit","in":"query","required":false,"schema":{"type":"string"},"description":"Maximum channels to return (1–50, default 10).","example":"10"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","description":"Matching visible imported channels.","properties":{"total":{"type":"number","description":"Number of channels returned; not a corpus-wide match count."},"channels":{"type":"array","description":"Channels available to the caller.","items":{"type":"object","description":"Channel discovery result.","properties":{"id":{"type":"string","description":"YouTube channel ID, usable in searches and questions."},"title":{"type":"string","description":"Channel title."},"handle":{"type":"string","description":"Canonical @handle when known."},"thumbnail":{"type":"string","description":"Channel thumbnail URL."},"videoCount":{"type":"number","description":"Number of public imported videos."},"transcriptVideoCount":{"type":"number","description":"Number of public videos with a saved transcript. This does not guarantee vector indexing is complete."},"url":{"type":"string","description":"Channel page on AskChannelAI."}}}}}}}}}}}},"/channels/import":{"post":{"operationId":"post_channels_import","summary":"Import a YouTube channel.","tags":["channels"],"x-auth":"user","x-side-effects":"enqueue","x-visibility":"external-beta","x-legacy-path":"/api/channels/import","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Channel import request.","required":["channelUrl"],"properties":{"channelUrl":{"type":"string","description":"YouTube channel URL, @handle, or UC… channel ID. Returns 404 if no such channel exists on YouTube."}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","description":"Import result.","properties":{"channel":{"type":"object","description":"The registered channel record."},"isNew":{"type":"string","description":"Whether the channel was newly registered by this call."},"runId":{"type":"string","description":"Import run ID when processing started immediately."},"queued":{"type":"string","description":"True when another import is in progress and this one was queued."}}}}}}}}},"/channels/{id}":{"get":{"operationId":"get_channels_id","summary":"Get channel details and processing state.","tags":["channels"],"x-auth":"user","x-side-effects":"read","x-visibility":"external-beta","x-legacy-path":"/api/channels/{id}","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","description":"Channel metadata and processing state.","properties":{"id":{"type":"string","description":"YouTube channel ID."},"handle":{"type":"string","description":"Channel @handle."},"title":{"type":"string","description":"Channel title."},"status":{"type":"string","description":"Import or processing status."},"total_videos":{"type":"number","description":"Known video count."},"last_updated":{"type":"string","description":"When the channel was last checked for new videos."}}}}}}}}},"/channels/{id}/import-progress":{"get":{"operationId":"get_channels_id_import-progress","summary":"Get cached channel import progress.","tags":["channels"],"x-auth":"user","x-side-effects":"enqueue","x-visibility":"external-beta","x-legacy-path":"/api/channels/{id}/import-progress","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","description":"Current import progress.","properties":{"status":{"type":"string","description":"Import status."},"imported":{"type":"number","description":"Imported video count."},"total":{"type":"number","description":"Expected total count."}}}}}}}}},"/channels/{id}/job-stats":{"get":{"operationId":"get_channels_id_job-stats","summary":"Live job throughput and ETA.","tags":["channels"],"x-auth":"user","x-side-effects":"read","x-visibility":"external-beta","x-legacy-path":"/api/channels/{id}/job-stats","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}},"/channels/{id}/search-quotes":{"get":{"operationId":"get_channels_id_search-quotes","summary":"Full-text quote search.","tags":["channels"],"x-auth":"user","x-side-effects":"read","x-visibility":"external-beta","x-legacy-path":"/api/channels/{id}/search-quotes","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"q","in":"query","required":true,"schema":{"type":"string"},"description":"Search query. Wrap in double quotes for an exact phrase search.","example":"airpods"},{"name":"page","in":"query","required":false,"schema":{"type":"string"},"description":"1-based page number.","example":"1"},{"name":"pageSize","in":"query","required":false,"schema":{"type":"string"},"description":"Results per page (1-50).","example":"10"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","description":"Paginated quote matches.","properties":{"enabled":{"type":"string","description":"Whether this channel has a searchable transcript index."},"total":{"type":"number","description":"Total matching segments across all pages."},"page":{"type":"number","description":"Current page number."},"pageSize":{"type":"number","description":"Results returned per page."},"results":{"type":"array","description":"Matching transcript segments, highest-ranked first.","items":{"type":"object","description":"Quote match.","properties":{"videoId":{"type":"string","description":"YouTube video ID."},"videoTitle":{"type":"string","description":"Video title."},"startTime":{"type":"number","description":"Segment start time in seconds."},"text":{"type":"string","description":"Full transcript segment text."},"snippet":{"type":"string","description":"Highlighted snippet with matches wrapped in << >>."}}}}}}}}}}}},"/search-quotes-hybrid":{"get":{"operationId":"get_search-quotes-hybrid","summary":"Multi-channel hybrid quote search (FTS + dense + RRF). One Postgres FTS call for all channels.","tags":["search-quotes-hybrid"],"x-auth":"user","x-side-effects":"read","x-visibility":"external-beta","x-legacy-path":"/api/search-quotes-hybrid","parameters":[{"name":"channels","in":"query","required":true,"schema":{"type":"string"},"description":"Comma-separated channel ids or handles to search (required). Alias: channelIds. Max 50.","example":"UCxxx,UCyyy"},{"name":"q","in":"query","required":false,"schema":{"type":"string"},"description":"Primary search query / alias. Optional if aliases is set.","example":"NVDA"},{"name":"aliases","in":"query","required":false,"schema":{"type":"string"},"description":"Comma-separated probes OR'd across FTS + dense (e.g. NVDA,NVIDIA,英伟达). Combined with q.","example":"NVDA,NVIDIA,英伟达"},{"name":"page","in":"query","required":false,"schema":{"type":"string"},"description":"1-based page over the fused ranking.","example":"1"},{"name":"pageSize","in":"query","required":false,"schema":{"type":"string"},"description":"Results per page (1-50).","example":"10"},{"name":"mode","in":"query","required":false,"schema":{"type":"string"},"description":"Retrieval mode: hybrid (default), fts, or dense.","example":"hybrid"},{"name":"sinceDays","in":"query","required":false,"schema":{"type":"string"},"description":"Optional lookback: only videos uploaded within this many days.","example":"30"},{"name":"minDenseScore","in":"query","required":false,"schema":{"type":"string"},"description":"Floor on the dense (Pinecone cosine) branch only, applied before RRF. Default 0. Alias: minScore. Does not filter results[].score.","example":"0"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","description":"Hybrid-fused quote matches across channels.","properties":{"enabled":{"type":"boolean","description":"Whether a valid retrieval query was attempted; an empty index can return no matches."},"total":{"type":"number","description":"Public fused candidates before pagination; not a corpus-wide match count."},"totalScope":{"type":"string","description":"The bounded candidate pool covered by total.","enum":["retrieved_candidates"]},"scoreType":{"type":"string","description":"Score scale: rank fusion in hybrid, lexical rank in fts, cosine in dense.","enum":["rrf","ts_rank","cosine"]},"page":{"type":"number","description":"Current page number."},"pageSize":{"type":"number","description":"Results returned per page."},"mode":{"type":"string","description":"Mode that produced the ranking.","enum":["hybrid","fts","dense"]},"channelIds":{"type":"array","description":"Resolved channel ids included in the search.","items":{"type":"string","description":"YouTube channel id."}},"branches":{"type":"object","description":"Raw candidate counts per branch before fusion.","properties":{"fts":{"type":"number","description":"FTS branch hit count."},"dense":{"type":"number","description":"Dense branch hit count."}}},"results":{"type":"array","description":"Fused quote hits, highest ranking score first.","items":{"type":"object","description":"Hybrid quote match.","properties":{"channelId":{"type":"string","description":"YouTube channel id that produced this hit."},"videoId":{"type":"string","description":"YouTube video ID."},"videoTitle":{"type":"string","description":"Video title."},"startTime":{"type":"number","description":"Segment start time in seconds."},"endTime":{"type":"number","description":"Segment end time in seconds."},"text":{"type":"string","description":"Transcript passage text."},"snippet":{"type":"string","description":"Passage with alias hits wrapped in << >> for highlighting."},"score":{"type":"number","description":"Ranking score — RRF when mode=hybrid (Σ 1/(k+rank)); ts_rank when mode=fts; Pinecone cosine when mode=dense. Not comparable across modes."},"sources":{"type":"array","description":"Which branches contributed this hit.","items":{"type":"string","description":"Branch id.","enum":["fts","dense"]}},"uploadDate":{"type":"string","description":"Video upload time (ISO), when known."}}}}}}}}}}}},"/channel/qa/{channelId}":{"post":{"operationId":"post_channel_qa_channelId","summary":"Ask a cited question over a channel.","tags":["channel"],"x-auth":"user","x-side-effects":"write","x-visibility":"external-beta","x-legacy-path":"/api/channel/qa/{channelId}","parameters":[{"name":"channelId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Channel question request.","required":["question"],"properties":{"question":{"type":"string","description":"Question to answer from channel evidence."},"platform":{"type":"string","description":"Optional. Pre-render the answer for a chat platform; adds a ready-to-send `rendered` field to the response (the raw answer + citations are still returned).","enum":["telegram","discord","whatsapp","slack"]}}}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","description":"Answer with citations.","properties":{"answer":{"type":"string","description":"The generated answer as clean prose. Contains NO inline citation markers — every citation is in the separate citations array."},"citations":{"type":"array","description":"Citations supporting the answer, each anchored to a video timestamp.","items":{"type":"object","description":"Citation.","properties":{"videoId":{"type":"string","description":"YouTube video ID."},"videoTitle":{"type":"string","description":"Video title."},"startTime":{"type":"number","description":"Citation start time in seconds."},"endTime":{"type":"number","description":"Citation end time in seconds."},"text":{"type":"string","description":"The cited transcript passage."},"url":{"type":"string","description":"Deep link to the video at the cited timestamp."}}}},"rendered":{"type":"string","description":"Only present when `platform` is set: the answer pre-formatted for that chat app (HTML for telegram, mrkdwn for slack/whatsapp). For discord this is a { content, embeds } object instead of a string."}}}}}}}}},"/search/instant":{"get":{"operationId":"get_search_instant","summary":"Instant text search over videos.","tags":["search"],"x-auth":"user","x-side-effects":"read","x-visibility":"external-beta","x-legacy-path":"/api/search/instant","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string"},"description":"Keyword search query.","example":"macbook pro"},{"name":"channel_id","in":"query","required":false,"schema":{"type":"string"},"description":"Optional channel handle or ID scope. The @ prefix is optional — apple and @apple are equivalent.","example":"apple"},{"name":"limit","in":"query","required":false,"schema":{"type":"string"},"description":"Maximum results to return.","example":"20"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}}}}}}}