{"openapi":"3.1.0","info":{"title":"BidClub Open Podcast Transcript API","version":"2026.9.22.2","description":"Public reads are keyless, account-free, CORS *. Pitch administrator POST routes use a verified private session. All /api/* responses carry X-Robots-Tag: noindex. Agents: POST /api/mcp speaks Model Context Protocol over Streamable HTTP and exposes the same library as tools (rate limited, JSON-RPC, deliberately not modelled here as it is not a REST resource). Private: POST /api/revalidate (publish hook, Bearer auth — not documented here). Direct SQL-style queries: public read-only PostgREST endpoint documented at https://bidclub.ai/api-docs (PostgREST self-serves its own OpenAPI at {SUPABASE_URL}/rest/v1/).","x-bilingual":"All editorial content is bilingual EN/ZH via *_alt fields; lang/lang_alt mark each edition's language."},"servers":[{"url":"https://bidclub.ai"}],"paths":{"/api/pitch/feed":{"get":{"operationId":"getPitchFeedPage","summary":"Paginated published pitches","description":"Public Tweetroom pitches using opaque keyset cursors. Follow nextCursor until it is null. Cursor identity includes the sort, asset filter, and normalized bilingual search query; a cursor cannot be reused with different filters.","parameters":[{"name":"sort","in":"query","required":false,"schema":{"type":"string","enum":["date","quality"],"default":"date"}},{"name":"asset","in":"query","required":false,"schema":{"type":"string","enum":["all","stock","token"],"default":"all"}},{"name":"query","in":"query","required":false,"description":"At most 512 characters. Whitespace-separated terms are ANDed across author, ticker, and English/Chinese thesis text; one leading $ per term is ignored.","schema":{"type":"string","maxLength":512,"default":""}},{"name":"cursor","in":"query","required":false,"description":"Opaque continuation token returned by the preceding page.","schema":{"type":"string","maxLength":8192}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"One ready page, which may legitimately be empty.","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}},"Access-Control-Allow-Origin":{"schema":{"type":"string","const":"*"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PitchPage"}}}},"400":{"description":"Malformed, duplicate, unknown, or mismatched input. Body: {\"error\":\"invalid_request\"}.","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}},"Access-Control-Allow-Origin":{"schema":{"type":"string","const":"*"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database or validated public projection is unavailable. The response has an empty posts array, a null cursor, and unavailable status.","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}},"Access-Control-Allow-Origin":{"schema":{"type":"string","const":"*"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PitchPage"}}}}}},"options":{"summary":"CORS preflight","description":"Returns 204 with permissive CORS headers. No body.","responses":{"204":{"description":"Preflight accepted."}}}},"/api/pitch/auth/request":{"post":{"operationId":"requestPitchAdminOtp","summary":"Request a Pitch administrator email code","description":"Returns the same generic response for every valid email shape. Only pre-provisioned administrators are eligible; this flow should not create users.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"type":"string","format":"email","maxLength":254}}}}}},"responses":{"202":{"description":"Generic accepted response.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"message":{"type":"string"}}}}}},"400":{"description":"Malformed request.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Origin rejected.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body exceeds 8 KiB.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Content type is not application/json.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/pitch/auth/verify":{"post":{"operationId":"verifyPitchAdminOtp","summary":"Verify a Pitch administrator email code","description":"Creates a private server-managed session only after fresh identity verification and current administrator membership.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["email","token"],"properties":{"email":{"type":"string","format":"email","maxLength":254},"token":{"type":"string","pattern":"^[0-9]{6}$"}}}}}},"responses":{"200":{"description":"Authenticated.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"type":"object","required":["authenticated"],"properties":{"authenticated":{"const":true}}}}}},"400":{"description":"Malformed request.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Origin, code, identity, or membership rejected.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body exceeds 8 KiB.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Content type is not application/json.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/pitch/auth/logout":{"post":{"operationId":"logoutPitchAdmin","summary":"End the Pitch administrator session","responses":{"200":{"description":"Session cleared.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"type":"object","required":["authenticated"],"properties":{"authenticated":{"const":false}}}}}},"403":{"description":"Origin rejected.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Provider or transport unavailable; the browser session may remain active.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/pitch/tracking":{"post":{"operationId":"importPitchTrackingHandles","summary":"Import tracked Pitch handles","description":"Verified administrators may add up to 1,000 literal handles or profile URLs in at most 64 KiB. This updates tracking only and never starts retrieval.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["input"],"properties":{"input":{"type":"string","maxLength":65536}}}}}},"responses":{"200":{"description":"Audited import result; no retrieval started.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["added","duplicates","invalid","handles"],"properties":{"added":{"type":"array","items":{"type":"string"}},"duplicates":{"type":"array","items":{"type":"string"}},"invalid":{"type":"array","items":{"type":"object","required":["value","reason"],"properties":{"value":{"type":"string"},"reason":{"type":"string"}}}},"handles":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"Malformed body or operation identifier.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Origin, identity, or membership rejected.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Operation identifier conflict.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Import input exceeds 64 KiB.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Content type is not application/json.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Authentication or tracking storage unavailable.","headers":{"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/shows":{"get":{"operationId":"listShows","summary":"Show registry","description":"The curated roster: identity, language, hosts, tracking state, and source URLs.","responses":{"200":{"description":"The complete registry.","headers":{"Cache-Control":{"schema":{"type":"string","const":"public, s-maxage=300, stale-while-revalidate=600"},"description":"Origin cache policy. The hosting CDN may consume shared-cache directives before forwarding this header to clients."},"Access-Control-Allow-Origin":{"schema":{"type":"string","const":"*"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShowList"}}}},"502":{"description":"Upstream database unavailable. Body: {\"error\": \"upstream unavailable\"}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"options":{"summary":"CORS preflight","description":"Returns 204 with permissive CORS headers. No body.","responses":{"204":{"description":"Preflight accepted."}}}},"/api/v1/episodes":{"get":{"operationId":"listEpisodes","summary":"Paginated episode metadata","description":"Newest-first metadata without the long Markdown bodies. Follow pagination.next_offset until it is null.","parameters":[{"name":"show","in":"query","required":false,"description":"Exact show id from /api/v1/shows.","schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$"}},{"name":"lang","in":"query","required":false,"description":"Source language of the episode. Input is uppercased before matching.","schema":{"type":"string","enum":["EN","ZH"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0}},{"name":"asset","in":"query","required":false,"description":"Comma-separated asset ids. Values are ORed within this facet; supplied facets are ANDed. Omitting the facet or selecting every id leaves it unfiltered, including untagged episodes; none returns no episodes.","schema":{"type":"string","pattern":"^(?:none|(?:equities|crypto|vc_pe|other)(?:,(?:equities|crypto|vc_pe|other))*)$"}},{"name":"sector","in":"query","required":false,"description":"Comma-separated sector ids. Values are ORed within this facet; supplied facets are ANDed. Omitting the facet or selecting every id leaves it unfiltered, including untagged episodes; none returns no episodes.","schema":{"type":"string","pattern":"^(?:none|(?:ai_software|semis|robotics|biotech|consumer|finance|blockchain|energy|space_defense)(?:,(?:ai_software|semis|robotics|biotech|consumer|finance|blockchain|energy|space_defense))*)$"}},{"name":"focus","in":"query","required":false,"description":"Comma-separated focus ids. Values are ORed within this facet; supplied facets are ANDed. Omitting the facet or selecting every id leaves it unfiltered, including untagged episodes; none returns no episodes.","schema":{"type":"string","pattern":"^(?:none|(?:investing|company_building|technical|macro|policy)(?:,(?:investing|company_building|technical|macro|policy))*)$"}}],"responses":{"200":{"description":"One page of metadata.","headers":{"Cache-Control":{"schema":{"type":"string","const":"public, s-maxage=300, stale-while-revalidate=600"},"description":"Origin cache policy. The hosting CDN may consume shared-cache directives before forwarding this header to clients."},"Access-Control-Allow-Origin":{"schema":{"type":"string","const":"*"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EpisodeListResponse"}}}},"400":{"description":"invalid limit | invalid offset | invalid show | invalid lang | invalid {asset|sector|focus} tag: {id}","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream database unavailable. Body: {\"error\": \"upstream unavailable\"}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"options":{"summary":"CORS preflight","description":"Returns 204 with permissive CORS headers. No body.","responses":{"204":{"description":"Preflight accepted."}}}},"/api/v1/episodes/{slug}":{"get":{"operationId":"getEpisode","summary":"One full episode","description":"Editorial Markdown, transcript, alternate-language fields, and provenance. This is the correct route for bulk or automated content access — responses are CDN-cached with a configured shared-cache lifetime of 60 seconds and no stale-while-revalidate. Conditional-response validators are not guaranteed.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9](?:[a-z0-9-]{0,158}[a-z0-9])?$","maxLength":160}}],"responses":{"200":{"description":"The episode record.","headers":{"Cache-Control":{"schema":{"type":"string","const":"public, s-maxage=60"},"description":"Origin cache policy. The hosting CDN may consume shared-cache directives before forwarding this header to clients."},"Access-Control-Allow-Origin":{"schema":{"type":"string","const":"*"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Episode"}}}},"404":{"description":"No published episode with that slug. Body: {\"error\": \"not found\"}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream database unavailable. Body: {\"error\": \"upstream unavailable\"}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"options":{"summary":"CORS preflight","description":"Returns 204 with permissive CORS headers. No body.","responses":{"204":{"description":"Preflight accepted."}}}},"/api/v1/search":{"get":{"operationId":"searchEpisodes","summary":"Search the whole library","description":"Full-text search across titles, deks, TL;DRs, digests, and transcripts, newest first, capped at 20 results. When full-text finds nothing the query re-runs as a substring (ilike) sweep of title, title_orig, dek, and transcript_md — the path that matches CJK, which the 'simple' text search configuration cannot segment. That sweep is always bounded to the 300 newest episodes matching the other filters, and whenever it produces the returned results the response carries partial: true and searched_recent: 300. The same two fields appear when the full-text query itself times out and is retried inside that window. searched_recent is the window ceiling, not the number of rows actually scanned. A zero-result substring fallback may omit partial; an empty response therefore does not establish that no older substring matches exist.","parameters":[{"name":"q","in":"query","required":true,"description":"2–100 code points, no control characters.","schema":{"type":"string","minLength":2,"maxLength":100}},{"name":"time","in":"query","required":false,"description":"Publication window, measured on published_at with a calendar-date fallback for archives that expose no exact timestamp.","schema":{"type":"string","enum":["24h","7d","30d"]}},{"name":"person","in":"query","required":false,"description":"Exact participant name without the \"person:\" prefix; matched against the episode's chips. Up to 120 code points.","schema":{"type":"string","maxLength":120}},{"name":"show","in":"query","required":false,"description":"Exact show id, or the literal __untracked__ for every episode whose show is not part of the tracked roster.","schema":{"type":"string","oneOf":[{"pattern":"^[a-z0-9][a-z0-9_-]{0,63}$"},{"const":"__untracked__"}]}},{"name":"lang","in":"query","required":false,"description":"Source language. Input is uppercased before matching.","schema":{"type":"string","enum":["EN","ZH"]}},{"name":"len","in":"query","required":false,"description":"Duration bucket; min inclusive, max exclusive.","schema":{"type":"string","enum":["lt30","30to60","60to120","gt120"]}},{"name":"asset","in":"query","required":false,"description":"Comma-separated asset ids. Values are ORed within this facet; supplied facets are ANDed. Omitting the facet or selecting every id leaves it unfiltered, including untagged episodes; none returns no episodes.","schema":{"type":"string","pattern":"^(?:none|(?:equities|crypto|vc_pe|other)(?:,(?:equities|crypto|vc_pe|other))*)$"}},{"name":"sector","in":"query","required":false,"description":"Comma-separated sector ids. Values are ORed within this facet; supplied facets are ANDed. Omitting the facet or selecting every id leaves it unfiltered, including untagged episodes; none returns no episodes.","schema":{"type":"string","pattern":"^(?:none|(?:ai_software|semis|robotics|biotech|consumer|finance|blockchain|energy|space_defense)(?:,(?:ai_software|semis|robotics|biotech|consumer|finance|blockchain|energy|space_defense))*)$"}},{"name":"focus","in":"query","required":false,"description":"Comma-separated focus ids. Values are ORed within this facet; supplied facets are ANDed. Omitting the facet or selecting every id leaves it unfiltered, including untagged episodes; none returns no episodes.","schema":{"type":"string","pattern":"^(?:none|(?:investing|company_building|technical|macro|policy)(?:,(?:investing|company_building|technical|macro|policy))*)$"}}],"responses":{"200":{"description":"Matches, newest first.","headers":{"Cache-Control":{"schema":{"type":"string","const":"public, s-maxage=60"},"description":"Origin cache policy. The hosting CDN may consume shared-cache directives before forwarding this header to clients."},"Access-Control-Allow-Origin":{"schema":{"type":"string","const":"*"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"400":{"description":"q required | q must contain at least 2 characters | q must contain at most 100 printable characters | invalid time | invalid person | invalid show | invalid lang | invalid len | invalid {asset|sector|focus} tag: {id}","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream database unavailable. Body: {\"error\": \"upstream unavailable\"}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"options":{"summary":"CORS preflight","description":"Returns 204 with permissive CORS headers. No body.","responses":{"204":{"description":"Preflight accepted."}}}},"/api/feed-index":{"get":{"operationId":"getFeedIndex","summary":"Complete catalog in one response","description":"Every published episode as compact metadata. Complete at the current scale, with no pagination. Clients MUST tolerate a future `pagination` object and `Link` headers appearing in this response. Intended for occasional discovery or mirroring; conditional-response validators are not guaranteed. Query filters are not accepted; use the show/person paths or filter returned metadata locally.","responses":{"200":{"description":"The whole catalog.","headers":{"Cache-Control":{"schema":{"type":"string","const":"public, max-age=60, s-maxage=300, stale-while-revalidate=600"},"description":"Origin cache policy. The hosting CDN may consume shared-cache directives before forwarding this header to clients."},"Access-Control-Allow-Origin":{"schema":{"type":"string","const":"*"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedIndexResponse"}}}},"502":{"description":"Upstream database unavailable. Body: {\"error\": \"upstream unavailable\"}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"options":{"summary":"CORS preflight","description":"Returns 204 with permissive CORS headers. No body.","responses":{"204":{"description":"Preflight accepted."}}}},"/api/feed-index/person/{person}":{"get":{"operationId":"getFeedIndexByPerson","summary":"Catalog filtered to one participant","description":"Same shape as /api/feed-index, restricted to episodes tagged \"person:{person}\". The path segment is the bare name, URL-encoded; matching is exact.","parameters":[{"name":"person","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":120}}],"responses":{"200":{"description":"Matching catalog rows.","headers":{"Cache-Control":{"schema":{"type":"string","const":"public, max-age=60, s-maxage=300, stale-while-revalidate=600"},"description":"Origin cache policy. The hosting CDN may consume shared-cache directives before forwarding this header to clients."},"Access-Control-Allow-Origin":{"schema":{"type":"string","const":"*"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedIndexResponse"}}}},"400":{"description":"Empty, overlong, or control-character name. Body: {\"error\": \"invalid person\"}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream database unavailable. Body: {\"error\": \"upstream unavailable\"}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"options":{"summary":"CORS preflight","description":"Returns 204 with permissive CORS headers. No body.","responses":{"204":{"description":"Preflight accepted."}}}},"/api/feed-index/show/{show}":{"get":{"operationId":"getFeedIndexByShow","summary":"Catalog filtered to one show","description":"Same shape as /api/feed-index, restricted to a single show id.","parameters":[{"name":"show","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$"}}],"responses":{"200":{"description":"Matching catalog rows.","headers":{"Cache-Control":{"schema":{"type":"string","const":"public, max-age=60, s-maxage=300, stale-while-revalidate=600"},"description":"Origin cache policy. The hosting CDN may consume shared-cache directives before forwarding this header to clients."},"Access-Control-Allow-Origin":{"schema":{"type":"string","const":"*"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedIndexResponse"}}}},"400":{"description":"Body: {\"error\": \"invalid show\"}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Upstream database unavailable. Body: {\"error\": \"upstream unavailable\"}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"options":{"summary":"CORS preflight","description":"Returns 204 with permissive CORS headers. No body.","responses":{"204":{"description":"Preflight accepted."}}}},"/dl/{slug}/{file}":{"get":{"operationId":"downloadEpisodeArtifact","summary":"Download one episode as a file","description":"Assembles a Markdown, plain-text, or typeset PDF artifact for a single episode. Errors are short plain-text bodies, not JSON.","x-notes":"File routes can run for up to 300 seconds. Most responses use private, no-store; single-episode Markdown containing only a transcript is served inline with public, s-maxage=300, stale-while-revalidate=86400. PDF objects are reused from storage, while PDF responses and show ZIPs remain private, no-store. Automated clients MUST use the JSON API (/api/v1/episodes/{slug}) for polling or bulk access.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9](?:[a-z0-9-]{0,158}[a-z0-9])?$","maxLength":160}},{"name":"file","in":"path","required":true,"description":"summary = TL;DR + digest, transcript = transcript only, full = both. If only a summary or transcript exists, full returns that available section. md keeps Markdown structure, txt strips most of it, pdf is typeset.","schema":{"type":"string","pattern":"^(summary|transcript|full)\\.(md|txt|pdf)$"}},{"name":"lang","in":"query","required":false,"description":"Requested edition. Case-insensitive; any other value (including omission) means orig, the episode's source language. Translated summaries require both TL;DR and digest translations; otherwise the source summary is served. Transcripts always retain the source language, so full can combine translated summaries with source transcripts. Single-episode responses report summary fallback in X-BidClub-Language-Fallback; show ZIPs record it per episode in README.txt.","schema":{"type":"string","enum":["EN","ZH","orig"],"default":"orig"}}],"responses":{"200":{"description":"The artifact. Markdown containing only a transcript is inline; other artifacts are attachments.","headers":{"Content-Disposition":{"schema":{"type":"string"},"description":"inline; filename=\"…\" for Markdown containing only a transcript, including full.md when no summary exists. attachment otherwise."},"Cache-Control":{"schema":{"type":"string","enum":["private, no-store","public, s-maxage=300, stale-while-revalidate=86400"]},"description":"Origin cache policy. Markdown containing only a transcript uses the public policy; all other artifacts use private, no-store. The CDN may consume shared-cache directives."},"Access-Control-Expose-Headers":{"schema":{"type":"string","const":"Content-Disposition, Content-Length, Content-Range, X-BidClub-PDF, X-BidClub-PDF-Renderer, X-BidClub-Language, X-BidClub-Language-Fallback"}},"Content-Length":{"schema":{"type":"integer","minimum":0},"description":"Response body size in bytes when known before streaming."},"Content-Range":{"schema":{"type":"string"},"description":"Byte range served when the hosting layer answers a range request."},"X-BidClub-Language":{"schema":{"type":"string","enum":["EN","ZH"]},"description":"Summary language for summary/full artifacts, source language for transcript-only artifacts. A full artifact can contain a transcript in another language."},"X-BidClub-Language-Fallback":{"schema":{"type":"string","enum":["source","none"]},"description":"source when a requested translated summary was unavailable; none otherwise. This does not promise a translated transcript."},"X-BidClub-PDF":{"schema":{"type":"string"},"description":"PDF responses only: whether the object was cached or freshly rendered."},"X-BidClub-PDF-Renderer":{"schema":{"type":"string"},"description":"PDF responses only: renderer identity, part of the cache key."}},"content":{"text/markdown":{"schema":{"type":"string"}},"text/plain":{"schema":{"type":"string"}},"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Unknown slug, unsupported file name, or no content for the requested artifact.","content":{"text/plain":{"schema":{"type":"string"}}}},"502":{"description":"PDF is temporarily unavailable.","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/dl/show/{show}/{file}":{"get":{"operationId":"downloadShowArchive","summary":"Download a whole show as a ZIP","description":"Streams a ZIP containing available published artifacts for one show, plus a README.txt recording unavailable artifacts and language fallbacks. Assembly uses one database listing and is subject to its row cap; use paginated JSON for complete exports of large shows. A large PDF archive can take minutes. Errors are short plain-text bodies, not JSON.","x-notes":"File routes can run for up to 300 seconds. Most responses use private, no-store; single-episode Markdown containing only a transcript is served inline with public, s-maxage=300, stale-while-revalidate=86400. PDF objects are reused from storage, while PDF responses and show ZIPs remain private, no-store. Automated clients MUST use the JSON API (/api/v1/episodes/{slug}) for polling or bulk access.","parameters":[{"name":"show","in":"path","required":true,"description":"Show id in slug form. Note this path accepts hyphens only and at most 80 characters — a narrower pattern than the api show query parameter.","schema":{"type":"string","pattern":"^[a-z0-9](?:[a-z0-9-]{0,78}[a-z0-9])?$"}},{"name":"file","in":"path","required":true,"description":"summary = TL;DR + digest, transcript = transcript only, full = both. If only a summary or transcript exists, full returns that available section. md keeps Markdown structure, txt strips most of it, pdf is typeset.","schema":{"type":"string","pattern":"^(summary|transcript|full)\\.(md|txt|pdf)$"}},{"name":"lang","in":"query","required":false,"description":"Requested edition. Case-insensitive; any other value (including omission) means orig, the episode's source language. Translated summaries require both TL;DR and digest translations; otherwise the source summary is served. Transcripts always retain the source language, so full can combine translated summaries with source transcripts. Single-episode responses report summary fallback in X-BidClub-Language-Fallback; show ZIPs record it per episode in README.txt.","schema":{"type":"string","enum":["EN","ZH","orig"],"default":"orig"}}],"responses":{"200":{"description":"The streamed archive, as an attachment.","headers":{"Content-Disposition":{"schema":{"type":"string"},"description":"attachment; filename=\"…\" (RFC 5987 filename* is added for CJK names)."},"Cache-Control":{"schema":{"type":"string","const":"private, no-store"}},"Access-Control-Expose-Headers":{"schema":{"type":"string","const":"Content-Disposition, Content-Length, Content-Range, X-BidClub-PDF, X-BidClub-PDF-Renderer, X-BidClub-Language, X-BidClub-Language-Fallback"}},"Content-Length":{"schema":{"type":"integer","minimum":0},"description":"Response body size in bytes when known before streaming."},"Content-Range":{"schema":{"type":"string"},"description":"Byte range served when the hosting layer answers a range request."}},"content":{"application/zip":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Unknown show, unsupported file name, or no published episodes.","content":{"text/plain":{"schema":{"type":"string"}}}},"502":{"description":"Podcast download is temporarily unavailable.","content":{"text/plain":{"schema":{"type":"string"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}},"Pitch":{"type":"object","additionalProperties":false,"required":["id","handle","date","asset","tickers","title","quality","bullets","sourceUrl","wordCount","chinese"],"properties":{"id":{"type":"string","pattern":"^[0-9]+$"},"handle":{"type":"string","pattern":"^@[A-Za-z0-9_]{1,15}$"},"date":{"type":"string","format":"date-time"},"asset":{"type":"string","enum":["stock","token"]},"tickers":{"type":"array","maxItems":1,"items":{"type":"string","minLength":1,"maxLength":40}},"title":{"type":"string","minLength":1,"maxLength":200},"quality":{"type":"number","minimum":0,"maximum":1},"bullets":{"type":"array","minItems":2,"maxItems":4,"items":{"type":"string","minLength":1}},"sourceUrl":{"type":"string","format":"uri"},"wordCount":{"type":"integer","minimum":0},"chinese":{"type":["object","null"],"additionalProperties":false,"required":["title","bullets"],"properties":{"title":{"type":"string","minLength":1,"maxLength":200},"bullets":{"type":"array","minItems":2,"maxItems":4,"items":{"type":"string","minLength":1}}}}}},"PitchPage":{"type":"object","additionalProperties":false,"required":["posts","nextCursor","status"],"properties":{"posts":{"type":"array","items":{"$ref":"#/components/schemas/Pitch"}},"nextCursor":{"type":["string","null"]},"status":{"type":"string","enum":["ready","unavailable"]}}},"Show":{"type":"object","required":["id","name","lang","hosts","tracked","position","sources"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"lang":{"type":"string","description":"Primary language of the show, lower-case ISO code (e.g. \"en\", \"zh\")."},"hosts":{"type":["string","null"]},"tracked":{"type":"boolean","description":"Whether the show is on the actively-followed roster."},"position":{"type":"integer","description":"Display order in the registry."},"sources":{"type":"object","description":"Known upstream locations and the registry blurb.","properties":{"rss":{"type":["string","null"]},"youtube":{"type":["string","null"]},"xiaoyuzhou":{"type":["string","null"]},"site":{"type":["string","null"]},"description":{"type":["string","null"]},"description_source":{"type":["string","null"]},"other":{"type":["object","null"],"additionalProperties":{"type":"string"}}}}}},"ShowList":{"type":"object","required":["shows"],"properties":{"shows":{"type":"array","items":{"$ref":"#/components/schemas/Show"}}}},"EpisodeListItem":{"type":"object","required":["slug","show_id","title"],"properties":{"slug":{"type":"string","description":"Stable public identifier."},"show_id":{"type":"string","description":"Foreign key into /api/v1/shows."},"title":{"type":"string","description":"Complete canonical publisher title."},"display_title":{"type":["string","null"],"description":"Feed-only compression of an overlength title; null when unneeded."},"title_orig":{"type":["string","null"],"description":"Source-platform title when it differs."},"dek":{"type":["string","null"],"description":"One-line editorial standfirst."},"lang":{"type":["string","null"],"enum":["EN","ZH",null],"description":"Language of the primary editorial fields."},"date":{"type":["string","null"],"description":"Publication calendar date, YYYY-MM-DD."},"published_at":{"type":["string","null"],"description":"Exact publication timestamp (RFC 3339); null for date-only archives."},"duration_min":{"type":["number","null"],"description":"Episode length in minutes."},"source_url":{"type":["string","null"]},"source_label":{"type":["string","null"]},"rss_url":{"type":["string","null"]},"youtube_id":{"type":["string","null"]},"youtube_url":{"type":["string","null"]},"provenance":{"type":"array","description":"Ordered [stage, tool] pairs recording how the episode was produced.","items":{"type":"array","items":{"type":"string"},"minItems":2,"maxItems":2}},"chips":{"type":"array","items":{"type":"string"},"description":"Participant tags, each prefixed \"person:\" (e.g. \"person:Jensen Huang\")."},"tag_assets":{"type":"array","maxItems":2,"uniqueItems":true,"items":{"type":"string","enum":["equities","crypto","vc_pe","other"]},"description":"Zero to two closed asset taxonomy ids, primary first. Empty is valid even after classification."},"tag_sectors":{"type":"array","maxItems":2,"uniqueItems":true,"items":{"type":"string","enum":["ai_software","semis","robotics","biotech","consumer","finance","blockchain","energy","space_defense"]},"description":"Zero to two closed sector taxonomy ids, primary first. Empty is valid even after classification."},"tag_focus":{"type":"array","maxItems":2,"uniqueItems":true,"items":{"type":"string","enum":["investing","company_building","technical","macro","policy"]},"description":"Zero to two closed editorial-focus taxonomy ids, primary first. Empty is valid even after classification."},"tagging_status":{"type":"string","enum":["pending","classified","failed","skipped"],"description":"Classification state. Non-classified episodes have empty tag arrays; classified does not require a tag in every facet."},"tagging_version":{"type":["string","null"],"description":"Taxonomy version used for classification; nonblank when tagging_status is classified, otherwise it may be null."},"shows":{"type":["object","null"],"properties":{"name":{"type":"string"}}}}},"Episode":{"type":"object","required":["slug","show_id","title"],"properties":{"slug":{"type":"string","description":"Stable public identifier."},"show_id":{"type":"string","description":"Foreign key into /api/v1/shows."},"title":{"type":"string","description":"Complete canonical publisher title."},"display_title":{"type":["string","null"],"description":"Feed-only compression of an overlength title; null when unneeded."},"title_orig":{"type":["string","null"],"description":"Source-platform title when it differs."},"dek":{"type":["string","null"],"description":"One-line editorial standfirst."},"lang":{"type":["string","null"],"enum":["EN","ZH",null],"description":"Language of the primary editorial fields."},"date":{"type":["string","null"],"description":"Publication calendar date, YYYY-MM-DD."},"published_at":{"type":["string","null"],"description":"Exact publication timestamp (RFC 3339); null for date-only archives."},"duration_min":{"type":["number","null"],"description":"Episode length in minutes."},"source_url":{"type":["string","null"]},"source_label":{"type":["string","null"]},"rss_url":{"type":["string","null"]},"youtube_id":{"type":["string","null"]},"youtube_url":{"type":["string","null"]},"provenance":{"type":"array","description":"Ordered [stage, tool] pairs recording how the episode was produced.","items":{"type":"array","items":{"type":"string"},"minItems":2,"maxItems":2}},"chips":{"type":"array","items":{"type":"string"},"description":"Participant tags, each prefixed \"person:\" (e.g. \"person:Jensen Huang\")."},"tag_assets":{"type":"array","maxItems":2,"uniqueItems":true,"items":{"type":"string","enum":["equities","crypto","vc_pe","other"]},"description":"Zero to two closed asset taxonomy ids, primary first. Empty is valid even after classification."},"tag_sectors":{"type":"array","maxItems":2,"uniqueItems":true,"items":{"type":"string","enum":["ai_software","semis","robotics","biotech","consumer","finance","blockchain","energy","space_defense"]},"description":"Zero to two closed sector taxonomy ids, primary first. Empty is valid even after classification."},"tag_focus":{"type":"array","maxItems":2,"uniqueItems":true,"items":{"type":"string","enum":["investing","company_building","technical","macro","policy"]},"description":"Zero to two closed editorial-focus taxonomy ids, primary first. Empty is valid even after classification."},"tagging_status":{"type":"string","enum":["pending","classified","failed","skipped"],"description":"Classification state. Non-classified episodes have empty tag arrays; classified does not require a tag in every facet."},"tagging_version":{"type":["string","null"],"description":"Taxonomy version used for classification; nonblank when tagging_status is classified, otherwise it may be null."},"thumbnail_url":{"type":["string","null"]},"title_alt":{"type":["string","null"],"description":"Title in the alternate language (lang_alt)."},"display_title_alt":{"type":["string","null"]},"dek_alt":{"type":["string","null"]},"lang_alt":{"type":["string","null"],"enum":["EN","ZH",null],"description":"Language of every *_alt field."},"tldr_md":{"type":["string","null"],"description":"TL;DR bullets as Markdown."},"digest_md":{"type":["string","null"],"description":"Structured digest as Markdown."},"transcript_md":{"type":["string","null"],"description":"Full speaker-attributed transcript."},"tldr_md_alt":{"type":["string","null"]},"digest_md_alt":{"type":["string","null"]},"shows":{"type":["object","null"],"properties":{"name":{"type":"string"},"hosts":{"type":["string","null"]}}}}},"SearchResult":{"type":"object","required":["slug","show_id","title"],"properties":{"slug":{"type":"string","description":"Stable public identifier."},"show_id":{"type":"string","description":"Foreign key into /api/v1/shows."},"title":{"type":"string","description":"Complete canonical publisher title."},"display_title":{"type":["string","null"],"description":"Feed-only compression of an overlength title; null when unneeded."},"title_alt":{"type":["string","null"],"description":"Title in the alternate language (lang_alt)."},"display_title_alt":{"type":["string","null"]},"title_orig":{"type":["string","null"],"description":"Source-platform title when it differs."},"dek":{"type":["string","null"],"description":"One-line editorial standfirst."},"dek_alt":{"type":["string","null"]},"chips":{"type":"array","items":{"type":"string"},"description":"Participant tags, each prefixed \"person:\" (e.g. \"person:Jensen Huang\")."},"lang":{"type":["string","null"],"enum":["EN","ZH",null],"description":"Language of the primary editorial fields."},"date":{"type":["string","null"],"description":"Publication calendar date, YYYY-MM-DD."},"published_at":{"type":["string","null"],"description":"Exact publication timestamp (RFC 3339); null for date-only archives."},"duration_min":{"type":["number","null"],"description":"Episode length in minutes."},"youtube_id":{"type":["string","null"]},"youtube_url":{"type":["string","null"]},"thumbnail_url":{"type":["string","null"]},"source_url":{"type":["string","null"]},"source_label":{"type":["string","null"]},"rss_url":{"type":["string","null"]},"tag_assets":{"type":"array","maxItems":2,"uniqueItems":true,"items":{"type":"string","enum":["equities","crypto","vc_pe","other"]},"description":"Zero to two closed asset taxonomy ids, primary first. Empty is valid even after classification."},"tag_sectors":{"type":"array","maxItems":2,"uniqueItems":true,"items":{"type":"string","enum":["ai_software","semis","robotics","biotech","consumer","finance","blockchain","energy","space_defense"]},"description":"Zero to two closed sector taxonomy ids, primary first. Empty is valid even after classification."},"tag_focus":{"type":"array","maxItems":2,"uniqueItems":true,"items":{"type":"string","enum":["investing","company_building","technical","macro","policy"]},"description":"Zero to two closed editorial-focus taxonomy ids, primary first. Empty is valid even after classification."},"tagging_status":{"type":"string","enum":["pending","classified","failed","skipped"],"description":"Classification state. Non-classified episodes have empty tag arrays; classified does not require a tag in every facet."},"tagging_version":{"type":["string","null"],"description":"Taxonomy version used for classification; nonblank when tagging_status is classified, otherwise it may be null."},"shows":{"type":["object","null"],"properties":{"name":{"type":"string"}}}}},"FeedIndexItem":{"type":"object","required":["slug","show_id","show","title"],"properties":{"slug":{"type":"string","description":"Stable public identifier."},"show_id":{"type":"string","description":"Foreign key into /api/v1/shows."},"show":{"type":"string","description":"Show display name, flattened."},"title":{"type":"string","description":"Complete canonical publisher title."},"display_title":{"type":["string","null"],"description":"Feed-only compression of an overlength title; null when unneeded."},"title_alt":{"type":["string","null"],"description":"Title in the alternate language (lang_alt)."},"display_title_alt":{"type":["string","null"]},"title_orig":{"type":["string","null"],"description":"Source-platform title when it differs."},"dek":{"type":["string","null"],"description":"One-line editorial standfirst."},"dek_alt":{"type":["string","null"]},"chips":{"type":"array","items":{"type":"string"},"description":"Participant tags, each prefixed \"person:\" (e.g. \"person:Jensen Huang\")."},"lang":{"type":["string","null"],"enum":["EN","ZH",null],"description":"Language of the primary editorial fields."},"date":{"type":["string","null"],"description":"Publication calendar date, YYYY-MM-DD."},"published_at":{"type":["string","null"],"description":"Exact publication timestamp (RFC 3339); null for date-only archives."},"duration_min":{"type":["number","null"],"description":"Episode length in minutes."},"youtube_id":{"type":["string","null"]},"thumbnail_url":{"type":["string","null"]},"source_url":{"type":["string","null"]},"source_label":{"type":["string","null"]},"tag_assets":{"type":"array","maxItems":2,"uniqueItems":true,"items":{"type":"string","enum":["equities","crypto","vc_pe","other"]},"description":"Zero to two closed asset taxonomy ids, primary first. Empty is valid even after classification."},"tag_sectors":{"type":"array","maxItems":2,"uniqueItems":true,"items":{"type":"string","enum":["ai_software","semis","robotics","biotech","consumer","finance","blockchain","energy","space_defense"]},"description":"Zero to two closed sector taxonomy ids, primary first. Empty is valid even after classification."},"tag_focus":{"type":"array","maxItems":2,"uniqueItems":true,"items":{"type":"string","enum":["investing","company_building","technical","macro","policy"]},"description":"Zero to two closed editorial-focus taxonomy ids, primary first. Empty is valid even after classification."},"tagging_status":{"type":"string","enum":["pending","classified","failed","skipped"],"description":"Classification state. Non-classified episodes have empty tag arrays; classified does not require a tag in every facet."},"tagging_version":{"type":["string","null"],"description":"Taxonomy version used for classification; nonblank when tagging_status is classified, otherwise it may be null."}}},"Pagination":{"type":"object","required":["limit","offset","next_offset"],"properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"next_offset":{"type":["integer","null"],"description":"Offset of the next page, or null on the final page."}}},"EpisodeListResponse":{"type":"object","required":["episodes","pagination","artifacts"],"properties":{"episodes":{"type":"array","items":{"$ref":"#/components/schemas/EpisodeListItem"}},"pagination":{"$ref":"#/components/schemas/Pagination"},"artifacts":{"type":"string","description":"Constant hint string naming the per-episode JSON and file routes."}}},"SearchResponse":{"type":"object","required":["q","count","results"],"properties":{"q":{"type":"string","description":"The trimmed query as executed."},"count":{"type":"integer","description":"Number of results returned (at most 20)."},"results":{"type":"array","items":{"$ref":"#/components/schemas/SearchResult"}},"partial":{"type":"boolean","const":true,"description":"Present only when the results came from a windowed query rather than the whole library."},"searched_recent":{"type":"integer","const":300,"description":"Window ceiling in newest episodes matching the other filters, not the actual number scanned. Appears with partial."}}},"FeedIndexResponse":{"type":"object","required":["count","episodes"],"properties":{"count":{"type":"integer"},"episodes":{"type":"array","items":{"$ref":"#/components/schemas/FeedIndexItem"}}}}}}}